Request lifecycle and status
Every call that needs QuickBooks Desktop (any /v1/quickbooks-desktop/ operation, the health check and passthrough) becomes a request with an ID such as req_01j9x4m6v4c8k2t7q0r5s3w1zb. The ID comes back in the Daapi-Request-Id header of every response, success or error. You can read the request at any time with GET /v1/requests/{id}, and the dashboard request log shows the same data.
Calls rejected before they are queued, for example a validation error, a connection that has not finished setup or a disabled connection, are in the log too: a failed request with sentAt: null and the error code. They are logged when the call names an end user of the project that has a connection, which exists from its first auth session. Rate-limited calls and calls without a valid end user ID are not logged; keep the requestId from their error body.
Why requests queue
Section titled “Why requests queue”QuickBooks Desktop runs on your customer’s computer and cannot accept inbound connections. The QuickBooks Web Connector on that computer polls us instead. A request therefore waits in a queue for its connection until the Web Connector picks it up:
- Queued. We validate your call, translate it to qbXML and add it to the queue for that end user’s company file.
- Check-in. The Web Connector checks in. When work is waiting, it opens a QuickBooks session with the company file.
- Sent. The Web Connector passes the request to QuickBooks. From this point the request may change the company file.
- Answered. QuickBooks returns its response. We map it to JSON and finish the request as
succeededorfailed.
QuickBooks handles one request at a time per company file. Requests for one connection run in the order you sent them. Requests for different end users run independently.
How long requests take
Section titled “How long requests take”| Situation | Typical wait before QuickBooks starts on your request |
|---|---|
| Session already open (another request in the last 2 seconds, or a cursor read in the last 10 seconds) | Under a second |
| Connection idle | Up to 10 seconds, until the next check-in |
| Active in the last 10 minutes, session closed | Up to 3 seconds |
| No requests for 7 days | Up to 30 seconds for the first request |
| QuickBooks closed, “always allow” access granted | Add the time QuickBooks needs to start, often 10 to 60 seconds |
After a request finishes, the session stays open for about 2 seconds so a follow-up request skips the check-in wait. While a cursor has more pages, the session stays open for its 10-second idle window. A busy session closes after 2 minutes of continuous work and reopens at the next check-in, so other Web Connector applications on the same computer get their turn. While a session is open, QuickBooks treats the company file as in use by your app: closing the company, exiting QuickBooks or restoring a backup works again once the session closes, normally about 2 seconds after the last request. Send related requests back to back, and finish or abandon a cursor promptly, so the file is released quickly. If your customer sees QuickBooks refuse to close the file, send them QuickBooks says the company file is being used by another application.
QuickBooks itself sets the speed of large reads and reports. Page large lists (Pagination) and filter reports by date.
Sync mode (default)
Section titled “Sync mode (default)”By default your HTTP call waits for QuickBooks’ answer, up to Daapi-Timeout-Seconds seconds. The default is 90 seconds (60 for the health check); you can send any integer from 1 to 300.
| What happens | Response |
|---|---|
| QuickBooks answered | 200 or 201 with the result, or the mapped error |
| The end user has not finished setup | 409 INTEGRATION_CONNECTION_NOT_SET_UP, immediately |
| The Web Connector has not checked in for over a minute | 503 INTEGRATION_CONNECTION_NOT_ACTIVE, immediately, and nothing is queued |
| The Web Connector reported a QuickBooks error while your request waited (for example QuickBooks could not start, or the wrong file is open) | That error at once, such as 503 QBD_CANNOT_START. Nothing was sent. QBD_STARTING keeps waiting instead. |
| The Web Connector opened a session but QuickBooks has not answered it for 60 seconds (20 seconds if an earlier session already went unanswered) | 503 QBD_QUICKBOOKS_NOT_RESPONDING with details.diagnosis. Usually a dialog is open in QuickBooks. Nothing was sent. |
| The deadline passed before the request was sent | 504 REQUEST_TIMEOUT_NOT_SENT with details.diagnosis. The request is canceled and never reaches QuickBooks. Safe to retry. |
| The deadline passed after the request was sent | 504 QBD_REQUEST_TIMEOUT with outcome: "pending" and details.requestId. The request keeps running. Do not resend; read the result instead. |
The last row matters for writes. QuickBooks may still be applying your invoice when your HTTP call gives up. Resending would create a second invoice. Collect the result instead:
curl "https://api.desktopaccountingapi.com/v1/requests/req_01j9...?waitSeconds=60" \ -H "Authorization: Bearer $DAAPI_SECRET_KEY"waitSeconds (0 to 60) holds the call open until the request finishes, so you need no polling loop. The result field holds exactly the body the original call would have returned. The SDKs do this automatically when they receive QBD_REQUEST_TIMEOUT, and raise a pending error with the request ID only if their own timeout runs out first.
Async mode
Section titled “Async mode”For imports, background syncs and anything else where nobody is waiting, send Prefer: respond-async. The API answers 202 Accepted at once with the request resource, a Location: /v1/requests/{id} header and Preference-Applied: respond-async.
curl https://api.desktopaccountingapi.com/v1/quickbooks-desktop/customers \ -H "Authorization: Bearer $DAAPI_SECRET_KEY" \ -H "Daapi-End-User-Id: eu_01j9..." \ -H "Idempotency-Key: 0f8c2a3e-6d1b-4b8e-9a51-2f6c7d9e4a10" \ -H "Prefer: respond-async" \ -H "Daapi-Queue-Ttl-Seconds: 7200" \ -H "Content-Type: application/json" \ -d '{"name":"Northwind Traders"}'HTTP/1.1 202 AcceptedLocation: /v1/requests/req_01j9x4m6v4c8k2t7q0r5s3w1zbPreference-Applied: respond-asyncDaapi-Request-Id: req_01j9x4m6v4c8k2t7q0r5s3w1zbAn async request waits for the connection even while the computer is off: its status changes to waiting_for_connection when the Web Connector goes offline and back to queued at the next check-in. Daapi-Queue-Ttl-Seconds (10 to 86400, default 3600) sets the latest moment it may still be sent. If the connection does not come back in time, the request ends as failed with REQUEST_EXPIRED and a diagnosis, never having reached QuickBooks. Async requests are not affected by the 60-second unanswered-session rule; they keep waiting until their queue lifetime ends or QuickBooks failed three sessions in a row.
Find out when it finishes in either of two ways:
- Webhooks. Subscribe to
request.succeededandrequest.failed. See Webhooks. - Polling. Call
GET /v1/requests/{id}?waitSeconds=60until the status is final.
The request resource
Section titled “The request resource”{ "id": "req_01j9x4m6v4c8k2t7q0r5s3w1zb", "objectType": "request", "createdAt": "2026-10-05T16:03:59.002Z", "projectId": "proj_01j9...", "endUserId": "eu_01j9...", "connectionId": "conn_01j9...", "operationId": "qbd.customers.create", "method": "POST", "path": "/v1/quickbooks-desktop/customers", "mode": "async", "idempotencyKeyPresent": true, "previousRequestId": null, "status": "succeeded", "waitingReason": null, "queuePosition": null, "outcome": "applied", "sentAt": "2026-10-05T16:04:02.517Z", "completedAt": "2026-10-05T16:04:03.140Z", "queueTtlExpiresAt": "2026-10-05T18:03:59.002Z", "durationMs": 4138, "timings": { "queuedMs": 3515, "quickbooksMs": 623, "totalMs": 4138 }, "quickbooks": { "qbxmlVersion": "16.0", "messageSetId": "Vd9bq3kQx0m2R7yA1cT5wE", "statusCode": 0, "statusSeverity": "Info" }, "warnings": [], "recovered": false, "timeline": [ { "at": "2026-10-05T16:03:59.002Z", "status": "queued", "detail": "awaiting_check_in", "elapsedMs": 0 }, { "at": "2026-10-05T16:04:02.517Z", "status": "sent", "detail": null, "elapsedMs": 3515 }, { "at": "2026-10-05T16:04:03.140Z", "status": "succeeded", "detail": null, "elapsedMs": 4138 } ], "diagnosis": null, "error": null, "result": { "id": "80000042-1730313843", "objectType": "qbd_customer", "name": "Northwind Traders" }, "resultExpired": false}Statuses
Section titled “Statuses”| Status | Final | Meaning |
|---|---|---|
queued |
No | Accepted and waiting for its turn or the next check-in |
waiting_for_connection |
No | Async only: the connection is offline or QuickBooks is unavailable; the request goes out when it recovers, or expires |
sent |
No | Handed to QuickBooks. It can no longer be canceled |
succeeded |
Yes | QuickBooks accepted it, possibly with warnings |
failed |
Yes | Definitive failure; error explains why |
canceled |
Yes | Canceled before it was sent, by you or by a sync deadline |
outcome_unknown |
Yes, for you | A write was sent and its result could not be confirmed. Verify before writing again. See Idempotency and safe retries |
New statuses may appear; treat unknown ones as not final.
Waiting reasons
Section titled “Waiting reasons”While a request waits, waitingReason says why and queuePosition says how many requests are ahead (1 means next).
waitingReason |
Meaning |
|---|---|
awaiting_check_in |
Waiting for the Web Connector’s next check-in |
behind_other_requests |
Other requests for this company file go first |
opening_company_file |
QuickBooks is opening the file |
quickbooks_starting |
QuickBooks is starting |
connector_offline |
The computer or Web Connector is offline (async requests) |
quickbooks_unavailable |
The Web Connector checks in but QuickBooks cannot open the file; error previews the reason |
quickbooks_not_responding |
A Web Connector session is open but QuickBooks has not answered it for 20 seconds |
write_recovery_pending |
An earlier write’s outcome is unknown; later writes wait until that session ends, reads continue |
Timeline, timings and diagnosis
Section titled “Timeline, timings and diagnosis”Each timeline step has elapsedMs since the request was created. Besides status changes, the timeline records waiting (the waiting reason changed), diagnosed, and steps for cursors and write holds. timings splits the duration into time in the queue (queuedMs) and time in QuickBooks (quickbooksMs).
While a request waits or runs long, and after it timed out or expired, diagnosis ranks the probable causes from what we observed of the Web Connector and QuickBooks, with fixes for you and your customer. See Request diagnosis.
Outcome
Section titled “Outcome”outcome tells you whether a write changed the company file: applied, not_applied, pending, unknown, or not_applicable for reads. Errors carry the same field. It is the field to check before retrying a write.
Warnings
Section titled “Warnings”QuickBooks sometimes accepts a request and adds a warning, for example when it adjusts a value. We return the result with a Daapi-Warnings header that counts them, and record each warning (code, statusCode, message, path) in the request’s warnings array. The same array lists values the API had to read leniently from a damaged company file (QBD_VALUE_UNREADABLE, QBD_MARKUP_REPAIRED, with statusCode: null and the path of the value in result); see Damaged and unusual data.
Retention
Section titled “Retention”Request metadata, timelines and errors stay readable for 30 days. Request and response bodies stay for 15 days, after which result is null and resultExpired is true. A project can turn body capture off in the dashboard; bodies are then kept only until 24 hours after the request completes. See Security and data retention.
Listing and canceling requests
Section titled “Listing and canceling requests”GET /v1/requests lists a project’s requests newest first, filtered by endUserId, status (repeatable), operationId, createdAfter and createdBefore. Items are request summaries with an errorCode instead of the full error, timeline and result; read one request for the details. The list follows each status change within seconds.
POST /v1/requests/{id}/cancel cancels a request that is still queued or waiting_for_connection. It becomes canceled with REQUEST_CANCELED, and a sync caller still waiting on it receives that error. Canceling an already canceled request returns it unchanged. Any other status returns 409 REQUEST_NOT_CANCELABLE with details.status, because a sent request may already be changing the company file.
Errors in this guide
Section titled “Errors in this guide”QBD_QUICKBOOKS_NOT_RESPONDING, REQUEST_TIMEOUT_NOT_SENT, QBD_REQUEST_TIMEOUT, REQUEST_EXPIRED, REQUEST_CANCELED, REQUEST_NOT_CANCELABLE, QBD_READ_INTERRUPTED, INTEGRATION_CONNECTION_NOT_ACTIVE.