Skip to main content
Give each logical tool execution one idempotency key and save it before sending the request. Reuse that key, tool, and input after a timeout. Deepline returns the saved result or follows the original provider job without launching another one. A new key requests new work and can incur another charge. Recovery is opt-in. Existing calls without a key keep their current behavior. The SDK checks that the server supports recovery before submitting keyed work. The normal call still waits for its result. You know the key before opening the connection, so Deepline does not need to return early to give you a recovery ID. If the connection drops, reuse that key. Lookup reads saved state; repeating the original execute request resumes waiting when recovery is possible. Synchronous tools can replay their saved response. Async tools must declare a recoverable launch/status/results lifecycle and run in completion-waiting mode. Keyed launch-only requests, including wait_for_completion: false, are rejected before provider dispatch. Tools without that lifecycle return IDEMPOTENCY_NOT_SUPPORTED; the error explains the supported alternative.

TypeScript

Use a caller-owned key when a worker may restart. Save both the key and the original input in your application’s job record before calling Deepline.
The high-level Deepline.connect() context’s tools.execute(toolId, input, options) accepts the same recovery options. For a short-lived script, { recover: true } generates a key. Save a caller-owned key when recovery must survive the script itself. To persist a generated key before any network request, provide an awaited onExecution callback:
If the callback fails, no execution is sent. Recoverable network failures and active executions are retried with the same key and input. After the first attempt needs recovery, the default reconnect budget is 15 minutes; recoveryTimeoutMs changes it. The first request keeps its normal timeout. Exhausting the local wait returns EXECUTION_RECOVERY_TIMEOUT with the key in publicDetails. It does not cancel the provider job. Resume later with the same key. The SDK retries connection failures between your client and Deepline, and EXECUTION_IN_PROGRESS responses. A terminal provider error is returned to the caller; recovery does not repeatedly submit a finished failed execution.

Inspect after a lost response

Lookup observes the execution. To resume a recoverable provider job, repeat tools.execute with the original tool, input, and key. The SDK’s recovery path does this using the original key. It never substitutes a new key after an error.

CLI

Supply a key when your scheduler already owns an execution identity:
emails.json contains the tool’s normal input, for example:
Use --recover to generate a key before dispatch. The CLI reports it on stderr so stdout remains machine-readable JSON. Keep that key to recover the same execution from another process. Running a fresh command with --recover reuses its saved key while an identical invocation is unfinished. After the command successfully writes its output, that pending record is removed; a subsequent --recover invocation starts new work. Use --idempotency-key for an explicit cross-process or cross-machine identity. --recovery-timeout-ms changes the recovery wait budget without changing execution identity. If local output generation fails after the server completes, the pending key is retained. Retry with the same input and response format to retrieve the saved result. For the same response format, changing a file destination does not create a new execution.

Raw HTTP

Send Idempotency-Key with the normal execute request:
Look up the same key in the same workspace:
The lookup route returns X-Deepline-Idempotency-Supported: true, including for an unused key’s 404 EXECUTION_NOT_FOUND. Check this before first dispatch when integrating with a server whose recovery support is unknown. A generic 404 from an older server does not establish support.

Key and input rules

The server hashes the canonical request with SHA-256. Object property order does not create a conflict; array order and values do. The fingerprint includes the tool and execution-affecting options. Keep the original payload when retrying, including wait behavior and metadata. Resume through the same SDK, CLI, or HTTP call shape. Response format and metadata headers also participate in comparison, so switching between the high-level SDK’s dataset response and the raw HTTP response can conflict. Key lookup can inspect the saved response without resubmitting those options. Use the original API key or caller identity for recovery. Another caller in the same workspace cannot read the execution; attempting to submit the same key from that caller conflicts. Rotating an API key does not transfer its saved executions to the new key. The key stays short even for a large batch. Send the normal payload once per HTTP attempt; the server computes its digest. A digest cannot reconstruct your input, and idempotency does not increase HTTP or provider batch limits. --input @file reads local JSON; it does not enable an unsupported provider file-upload action.

States and errors

Completed responses are replayable for 24 hours after completion. The key’s record remains after replay expiry, so reusing an expired key produces an explicit error. Active and ambiguous executions never age into permission to launch another call. Save results in your own durable store if you need them beyond the replay window. An idempotency key cannot guarantee an external provider’s outcome when the provider accepts work and the process dies before its job ID can be saved. Deepline preserves that uncertainty and refuses duplicate dispatch. Once a recoverable async job ID is durably saved, recovery follows that job.

Billing and Plays

Replay returns the original response and its Deepline billing fields. It does not create a new paid execution. Recovery state describes execution delivery; it does not change billing finality or turn missing billing into a zero charge. Use the billing status and amount in the execution response. For a recovered async job, the completed response refers to the original execution’s billing identity. Status and result-fetching calls do not replace the parent execution’s cost. Replaying a saved response does not refresh its settlement status: a saved queued observation can remain queued even after the charge posts. To read current usage for the result above, use its job_id with the client’s exact billing lookup:
The HTTP equivalent is GET /api/v2/usage/events?request_id=<job_id>. This reads usage; it does not resume execution. A missing event or an unset credit amount does not establish a zero charge. Use an explicit recorded amount, including zero and its outcome reason, when available. After a lost response, executions.getByKey(key) also returns requestId, the same server-owned ID as the original response’s job_id. You can pass it to client.billing.usageEvent() while the execution is running or its result is unknown. This lets you investigate billing without resubmitting paid work. Idempotency is scoped to one execution. It does not impose a shared spending limit across different keys or concurrent workers. Inside a Play, continue using ctx.tools.execute({ id, tool, input }). Plays already own run and call identities. A Play call’s id is not a public API idempotency key. Use the Play’s run ID and run APIs to recover the workflow; this direct-tool API does not change Play authoring syntax.