Skip to main content
Cancel a running or pending flow execution. Useful for aborting long-running jobs or stopping mistakenly triggered executions.

Endpoint

POST https://app.doclo.ai/api/v1/runs/{executionId}/cancel

Authentication

Requires an API key with executions:cancel scope.
Authorization: Bearer dc_live_your_api_key

Path Parameters

ParameterTypeDescription
executionIdstringRequired. The execution identifier to cancel

Request Body

No request body required. Send an empty POST request.

Response

Success (200 OK)

{
  "id": "exec_abc123def456",
  "flowId": "invoice-extractor",
  "version": "1.2.0",
  "status": "cancelled",
  "createdAt": "2024-02-20T15:30:00Z",
  "cancelledAt": "2024-02-20T15:30:45Z",
  "metadata": {
    "customerId": "cust_123"
  }
}

Response Fields

FieldTypeDescription
idstringExecution identifier
flowIdstringFlow that was being executed
versionstringFlow version
statusstringAlways cancelled on success
createdAtstringISO 8601 start timestamp
cancelledAtstringISO 8601 cancellation timestamp
metadataobjectCustom metadata from original request

Examples

cURL

curl -X POST https://app.doclo.ai/api/v1/runs/exec_abc123def456/cancel \
  -H "Authorization: Bearer dc_live_your_api_key"

TypeScript

async function cancelExecution(executionId: string) {
  const response = await fetch(
    `https://app.doclo.ai/api/v1/runs/${executionId}/cancel`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.DOCLO_API_KEY}`
      }
    }
  );

  if (!response.ok) {
    const { error } = await response.json();
    throw new Error(`Cancel failed: ${error.message}`);
  }

  const result = await response.json();
  console.log(`Execution ${result.id} cancelled at ${result.cancelledAt}`);
  return result;
}

With Timeout Handling

async function processWithTimeout(flowId: string, document: string, timeoutMs: number) {
  // Start execution
  const startResponse = await fetch(
    `https://app.doclo.ai/api/v1/flows/${flowId}/run`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        input: { document: { base64: document } }
      })
    }
  );

  const { id: executionId } = await startResponse.json();

  // Set up timeout
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(async () => {
      // Cancel execution on timeout
      await fetch(
        `https://app.doclo.ai/api/v1/runs/${executionId}/cancel`,
        {
          method: 'POST',
          headers: { 'Authorization': `Bearer ${apiKey}` }
        }
      );
      reject(new Error('Execution timed out and was cancelled'));
    }, timeoutMs);
  });

  // Poll for results
  const pollPromise = pollExecution(executionId);

  return Promise.race([pollPromise, timeoutPromise]);
}

Errors

StatusCodeDescription
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENAPI key missing executions:cancel scope
404EXECUTION_NOT_FOUNDExecution doesn’t exist
409ALREADY_CANCELLEDExecution was already cancelled
409NOT_CANCELLABLEExecution already completed or failed
429RATE_LIMITEDRate limit exceeded

Error Examples

Already Cancelled:
{
  "error": {
    "code": "ALREADY_CANCELLED",
    "message": "Execution exec_abc123 was already cancelled"
  }
}
Not Cancellable (completed):
{
  "error": {
    "code": "NOT_CANCELLABLE",
    "message": "Cannot cancel execution with status: completed"
  }
}

Cancellation Behavior

Original StatusCan CancelResult
pendingYesImmediately cancelled, no credits used
runningYesCancelled at next checkpoint, partial credits may apply
completedNoReturns 409 error
failedNoReturns 409 error
cancelledNoReturns 409 error
Cancelling a running execution may not be immediate. The execution will be cancelled at the next processing checkpoint. Some credits may be charged for work already completed.

Use Cases

User-Initiated Cancellation

Allow users to cancel their own uploads:
// In your API route
app.post('/api/cancel-extraction/:executionId', async (req, res) => {
  const { executionId } = req.params;
  const userId = req.user.id;

  // Verify user owns this execution
  const execution = await db.executions.findOne({
    executionId,
    userId
  });

  if (!execution) {
    return res.status(404).json({ error: 'Execution not found' });
  }

  // Cancel via Doclo API
  const response = await fetch(
    `https://app.doclo.ai/api/v1/runs/${executionId}/cancel`,
    {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${process.env.DOCLO_API_KEY}` }
    }
  );

  if (response.ok) {
    await db.executions.update(executionId, { status: 'cancelled' });
    return res.json({ success: true });
  }

  const { error } = await response.json();
  return res.status(response.status).json({ error: error.message });
});

Cleanup on Shutdown

Cancel pending executions during graceful shutdown:
const pendingExecutions = new Set<string>();

// Track started executions
async function startExecution(flowId: string, input: any) {
  const result = await runFlow(flowId, input);
  pendingExecutions.add(result.id);
  return result;
}

// Cleanup on shutdown
process.on('SIGTERM', async () => {
  console.log('Shutting down, cancelling pending executions...');

  await Promise.all(
    Array.from(pendingExecutions).map(async (executionId) => {
      try {
        await cancelExecution(executionId);
        console.log(`Cancelled ${executionId}`);
      } catch (error) {
        // May already be completed, ignore errors
      }
    })
  );

  process.exit(0);
});

Next Steps