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
| Parameter | Type | Description |
|---|
executionId | string | Required. 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
| Field | Type | Description |
|---|
id | string | Execution identifier |
flowId | string | Flow that was being executed |
version | string | Flow version |
status | string | Always cancelled on success |
createdAt | string | ISO 8601 start timestamp |
cancelledAt | string | ISO 8601 cancellation timestamp |
metadata | object | Custom 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
| Status | Code | Description |
|---|
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 403 | FORBIDDEN | API key missing executions:cancel scope |
| 404 | EXECUTION_NOT_FOUND | Execution doesn’t exist |
| 409 | ALREADY_CANCELLED | Execution was already cancelled |
| 409 | NOT_CANCELLABLE | Execution already completed or failed |
| 429 | RATE_LIMITED | Rate 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 Status | Can Cancel | Result |
|---|
pending | Yes | Immediately cancelled, no credits used |
running | Yes | Cancelled at next checkpoint, partial credits may apply |
completed | No | Returns 409 error |
failed | No | Returns 409 error |
cancelled | No | Returns 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