Error Recovery

Retry supported QuickBooks Create operations safely with idempotency keys, recover delayed results, and avoid duplicate records after a timeout.

A timeout does not tell you whether QuickBooks created the record. QuickBooks may have completed the operation while the response was delayed or your client stopped waiting. Retrying with a new identity can create a duplicate.

For supported Create operations, use an idempotency key to identify one logical operation across attempts. nXus associates that key with the original job so retries can wait for its result or reload an already-completed record.

Current scope

These examples describe the TypeScript SDK’s generic resource Create methods, including their withResponse variants, against an API deployment that supports Create idempotency. Check the endpoint’s API reference for the Idempotency-Key header. This guide does not claim automatic key generation or recovery parity for Python or .NET yet.

Automatic protection within one call

The TypeScript SDK generates a cryptographically random Idempotency-Key once per generic .create() call and reuses it for every automatic attempt. The key is sent as an HTTP header, never as a field in the JSON request body.

By default, the SDK allows two additional attempts (three total), with backoff and jitter. maxRetries: 0 disables automatic retries but does not remove the Create’s idempotency key.

This protection is per logical call. Calling .create() again without an explicit key generates a new key, even when the body is identical.

Persist a key for resumable workflows

Generate and save a key before the first attempt when an import, worker, or user action can resume later. Store the original payload and target connection with it. Reuse all three when retrying that operation, including after a process restart. Use a distinct key for each new logical Create, not one key for an entire batch containing different records.

Keys must be nonblank and no longer than 200 characters. Do not put credentials or customer data in them. Key identity is scoped to the tenant, connection, and Create operation; changing the endpoint or connection is not a replay of the same operation. Idempotency is not a permanent deduplication database: do not assume an old key remains protected indefinitely.

create-vendor.ts
123456789101112131415161718192021222324252627282930313233343536373839404142
import { NxusApiError, NxusClient } from 'nxus-qbd';

const nxus = new NxusClient({
apiKey: process.env.NXUS_API_KEY!,
connectionId: process.env.NXUS_CONNECTION_ID!,
});

// Load a key saved with this operation before its first attempt.
// The worker must also reload the same saved payload on a retry.
const operationKey = process.env.NXUS_CREATE_OPERATION_KEY;
if (!operationKey) throw new Error('A persisted Create operation key is required');

const request = { name: 'Wholesale Sync Vendor', isActive: true };

try {
const response = await nxus.vendors.withResponse.create(request, {
idempotencyKey: operationKey,
maxRetries: 2,
});

console.log({
vendorId: response.data.id,
status: response.status,
requestId: response.requestId,
});
} catch (error) {
if (error instanceof NxusApiError) {
console.error({
status: error.status,
code: error.code,
message: error.userMessage,
requestId: error.requestId,
});

  if (error.code === 'IDEMPOTENCY_KEY_REUSED') {
    // The saved key was used with a different payload. Stop and reconcile
    // the original operation; do not turn this into an automatic new Create.
  }

}
throw error;
}

For plain results, use nxus.vendors.create(request, options) with the same options. A caller-supplied headers: { 'Idempotency-Key': key } remains supported; the first-class idempotencyKey option takes precedence when both are supplied. Direct HTTP clients must supply the header themselves and implement bounded retries; do not assume they inherit SDK key generation or retry behavior.

How recovery works

  1. nXus reserves the Create job for the supplied key and payload.
  2. QuickBooks executes the job through Web Connector. A delayed response can outlast the REST request’s wait window.
  3. A dispatched job can enter RecoveryPending. A same-key retry waits on the original job instead of creating another Add job.
  4. If the original job is already completed, nXus uses its stored QuickBooks ID to query the record and return it through the normal Create response path. A replay can still return 201 Created; that status alone does not imply another record was created.

Completed replay can require another QuickBooks query, so QuickBooks and QWC must remain available. The returned record is a fresh read, not necessarily a byte-for-byte copy of the first response.

QuickBooks message-set recovery identifiers and clear requests are server-owned. Do not send newMessageSetID or oldMessageSetID from application code. There is no public errorRecover() method or recovery-status polling endpoint to call.

Retry decisions and timeout behavior

SignalBehavior
x-should-retry: trueThe SDK retries within its configured retry budget.
x-should-retry: falseThe SDK surfaces the error without another automatic attempt.
Header absentThe SDK falls back to network errors, HTTP 408/429, and HTTP 5xx; 409 is not retried by default.
Local SDK timeout on a protected generic CreateAutomatic attempts reuse the same key. Exhausting the retry budget still leaves the operation’s outcome uncertain.
409 with IDEMPOTENCY_KEY_REUSEDThe key was reused with a different payload. Reconcile the saved request; this is not a transient failure.

The standard Retry-After response header supplies backoff guidance, with error.retryAfter in seconds as a fallback. Keep application-level retries bounded too; do not wrap the SDK in an unlimited retry loop.

The local HTTP timeout is in milliseconds; serverTimeoutSeconds controls the backend’s QuickBooks wait window. Give the local timeout enough headroom to receive the backend’s timeout response. The live recovery drill observed 408 from the backend and a local SDK timeout with status: 0; a timeout is not proof that the underlying Create failed. See Latency Model.

Do not change keys to escape an uncertain result

If the response is missing or retry attempts are exhausted, preserve the original key, payload, and connection. A new key represents a new operation. Investigate or contact support when the result cannot be safely recovered. Do not infer Update, Delete, Void, Auth Session, Connection, or custom-field recovery support from this Create feature.

Diagnostics and verified behavior

Use verbose: true or a custom SDK logger to record request attempts, timeout events, and retry scheduling. Save the final status, error code, request ID, returned QuickBooks ID, and an application operation identifier. Redact API credentials, raw idempotency keys, and sensitive payloads before sharing logs.

A live test against a non-production QuickBooks company file on August 31, 2026 verified delayed QWC response handling, automatic retry after a backend timeout, completed-result replay after a client timeout, one record per logical Create, and server-side recovery-clear job completion.

That test held and then released the original QWC callback. It did not prove recovery after a permanently lost callback, a backend crash before completion was recorded, or retransmission of an Add request through QuickBooks message-set recovery. Those are separate resilience scenarios, not guarantees established by the delayed-response test.

Further reading