Skip to main content

Cancel a Job

Use this request to cancel a job that is still pending. You sign in with the wallet that paid for the job instead of an API key. A confirmed cancellation refunds the payment in full. To cancel an API-key job, see Cancel a Job in the Jobs API.

Endpoint​

DELETE https://api.speechischeap.com/v2/x402/jobs/:job_id

Don't add a trailing slash. A request to /v2/x402/jobs/:job_id/ returns 404 Not Found.

Authentication​

Cancellation uses the standard Sign-In With X (SIWX) extension of x402. It isn't a payment, and it doesn't use an API key. It takes two requests:

  1. Send the DELETE without a SIGN-IN-WITH-X header. The response is 402 with a base64-encoded PAYMENT-REQUIRED header. It's an auth-only challenge with accepts: [] and a sign-in-with-x extension. The challenge is scoped to this job and this action, has a one-use nonce, and expires after 300 seconds. Requesting it doesn't cancel the job or charge the wallet.
  2. Sign the challenge with the paying wallet. Send the DELETE again with the proof in the SIGN-IN-WITH-X header.

The service checks the challenge and that the signer is the wallet that paid. A public job ID or transaction hash isn't enough. Each verified challenge works once, so request a new one before you retry. The signer must support EIP-191 signMessage.

An API-key DELETE can't cancel an x402 job. It returns 403.

Request​

HeaderDescription
SIGN-IN-WITH-XThe signed challenge, encoded by the SDK. Send it on the second request only.

Example Requests​

Request the Challenge​

curl --include --request DELETE \
--url https://api.speechischeap.com/v2/x402/jobs/00000000-1111-4222-8333-555555555555-sic

Cancel with the Paying Wallet​

Install the packages. This Node.js 22 example uses the same SIC_X402_WALLET_MODULE as the example client, and its signer must be the wallet that paid:

npm install @x402/core@2.28.0 @x402/extensions@2.28.0
import { pathToFileURL } from 'node:url';
import { x402Client, x402HTTPClient } from '@x402/core/client';
import { createSIWxPayload, encodeSIWxHeader } from '@x402/extensions/sign-in-with-x';

// The signer that paid for the job. It needs signMessage, not only signTypedData.
const { signer } = await import(pathToFileURL(process.env.SIC_X402_WALLET_MODULE).href);
const jobId = '00000000-1111-4222-8333-555555555555-sic';
const url = `https://api.speechischeap.com/v2/x402/jobs/${encodeURIComponent(jobId)}`;

// Request the challenge.
const challenge = await fetch(url, { method: 'DELETE', redirect: 'error' });
if (challenge.status !== 402) throw new Error('Cancellation challenge unavailable');
const http = new x402HTTPClient(new x402Client());
const required = http.getPaymentRequiredResponse(name => challenge.headers.get(name));
const extension = required.extensions['sign-in-with-x'];
const chain = extension.supportedChains.find(item => item.chainId === 'eip155:8453' && item.type === 'eip191');
if (required.accepts.length || required.resource.url !== url || !chain || extension.info.uri !== url
|| extension.info.statement !== `Cancel Speech is Cheap transcription ${jobId}`
|| !extension.info.resources?.includes(`urn:speechischeap:cancel:${jobId}`)) throw new Error('Invalid auth-only challenge');

// Sign it with the paying wallet and send the proof.
const proof = await createSIWxPayload({ ...extension.info, ...chain }, signer, url);
const response = await fetch(url, {
method: 'DELETE', redirect: 'error',
headers: { 'SIGN-IN-WITH-X': encodeSIWxHeader(proof) }
});
console.log(response.status, await response.json());

Response​

The service uses standard HTTP status codes to indicate the outcome of your request.

Status CodeDescription
200 OKThe cancellation is confirmed. The service refunds the payment in full.
202 AcceptedThe service recorded the request but hasn't confirmed the cancellation.
401 UnauthorizedThe proof is invalid, expired, already used, or for another job. Request a new challenge.
402 Payment RequiredThe challenge, when you send no SIGN-IN-WITH-X header. The service charges nothing.
403 ForbiddenThe signer isn't the wallet that paid.
404 Not FoundNo paid job has this ID.
409 ConflictThe job already completed, failed, or was canceled.
503 Service UnavailableCancellation is temporarily unavailable. Try again.

Confirmed​

{
"id": "00000000-1111-4222-8333-555555555555-sic",
"output": {
"error": "Transcription canceled"
},
"status": "CANCELED",
"cancellation_status": "confirmed"
}

Requested​

The job may still finish. Check Get a Job, or wait for the job's outcome and refund updates at your webhook.

{
"id": "00000000-1111-4222-8333-555555555555-sic",
"output": {},
"status": "PENDING",
"cancellation_status": "requested"
}

Already Final​

status is the job's final status, and cancellation_status is terminal:

{
"id": "00000000-1111-4222-8333-555555555555-sic",
"status": "COMPLETED",
"cancellation_status": "terminal"
}

Errors​

The errors for this endpoint are listed under x402 API errors. The most likely are a 401 for a challenge that expired or was already used, a 403 for a signer that isn't the paying wallet, and a 409 for a job that already finished.

Notes​

  • Use the job id, not the purchase_id.
  • Only a pending job can be canceled. A request for a job that already finished returns 409 and doesn't change its payment.