Produce jobs and run durable handlers from Python.
Python SDK
Install the published getratchet package with pip install getratchet. Version 0.1.0 supports Python 3.10+ and uses the standard library. The HTTP quickstart and API reference are also available.
Produce a durable job
import { createRatchet } from '@getratchet/sdk';
const ratchet = createRatchet({
baseUrl: 'https://getratchet.app',
apiKey: process.env.GETRATCHET_INGEST_KEY!,
});
await ratchet.startRun('Customer onboarding');
await ratchet.enqueue({ customerId: 'cus_2048' }, {
name: 'send_welcome_email', version: '1', endpoint: 'Email service',
idempotencyKey: 'welcome:cus_2048', timeoutMs: 30_000, maxAttempts: 5,
});
await ratchet.finishRun();Use a project and environment scoped INGEST key. Store it in a server-side environment variable instead of source code.
Run a separate worker
import { createRatchet } from '@getratchet/sdk';
const worker = createRatchet({
baseUrl: 'https://getratchet.app',
apiKey: process.env.GETRATCHET_WORKER_KEY!,
});
worker.registerTool({
name: 'send_welcome_email', version: '1',
handler: async (input: { customerId: string }, { idempotencyKey, signal }) =>
sendWelcomeEmail(input, { idempotencyKey, signal }),
});
await worker.worker.start({ concurrency: 4, signal: shutdownSignal });Use a separate WORKER key authorized for the exact name@version handler. Call worker.worker.stop() for graceful shutdown, and set downstream network timeouts. Python cannot forcibly stop a blocked handler thread. A crash after an external side effect but before its report can repeat that effect; use destination-side idempotency.
The client also supports replay_preview(step_id) before replay(step_id, reason=...), and after_step_id with after_status='FAILED' or 'SUCCEEDED' for dependencies. Enqueue branches before finish_run(). See durable worker operations for retry and replay safety.
Durable retry policies and error decisions
Durable jobs support STANDARD (5 attempts, 30s initial, 15m cap), AGGRESSIVE (8 attempts, 5s initial, 5m cap), RELAXED (3 attempts, 120s initial, 30m cap), and CUSTOM. Built-ins double their delay with ±20% jitter; CUSTOM supplies maxAttempts, initialDelayMs, multiplier, maxDelayMs, and jitterPercent. Counts include the first execution. Caps precede jitter; the final safety limit is 24 hours.
A selected tool contract overrides the endpoint policy. Explicit enqueue maxAttempts overrides only the attempt count. Each new job snapshots the complete policy; later endpoint edits or contract publication do not change its policy or saved due time. Existing jobs without snapshot data keep the old preset behavior. Synthetic tests remain one attempt.
TypeScript handlers can throw RetryableError(message, { retryAfterMs }) or NonRetryableError(message) and register classifyError(error, context) for third-party failures. Python provides RetryableError(message, retry_after_ms=...), NonRetryableError, and classify_error. Unknown errors default to retryable; schema failures and cancellation remain non-retryable. A classifier failure is reported only to the local error callback and falls back to retryable.
Retry hints must be integer milliseconds from 1,000 to 86,400,000. The persisted delay is the greater of the normal jittered policy delay and the hint, capped at 24 hours; the hint itself is never jittered. An open circuit, pause, concurrency gate or attempt limit is never bypassed. Error objects, stacks and classifier metadata are not sent to the API.
Execution is at least once: a crash or lost report can repeat a completed side effect. Pass the stable job idempotency key to the destination and require destination-side deduplication for irreversible writes.
See custom policies and provider examples for endpoint/tool publication, validation limits, HTTP 429 with Retry-After, HTTP 503, non-retryable HTTP 400 validation failures, and unknown-error fallback.