Integration fundamentals
Production guidance for errors, retries, pagination, rate limits, observability, and safe API-key handling.
Integration fundamentals
Donutwork is a server-to-server JSON API. Build one small client layer that owns authentication, timeouts, error parsing, request correlation, and retry policy; keep business code independent from HTTP details.
Environments and versioning
Use the production origin and pin the API version in every URL:
https://api.hub.donutwork.com/2026-02-01Never construct requests from an unversioned base URL. Keep staging and production API keys in separate secret stores, and never reuse resource IDs across environments. The 2026-02-01 contract is currently served by the compatible 2024-08-01 implementation; clients should still send the documented version so future migrations are explicit.
Request conventions
- Send
Authorization: Bearer YOUR_API_KEYon every request. - Send
Content-Type: application/jsonwhen a JSON body is present. - Treat IDs as opaque strings. Do not infer type, creation time, or tenant from their shape.
- Use UTC ISO 8601 timestamps unless an endpoint documents a date-only value.
- Omit optional fields instead of sending empty strings unless clearing a value is explicitly supported.
- Never place secrets, credentials, tokens, or sensitive customer content in metadata fields.
All lookups are scoped to the company attached to the API key. An identifier copied from another company is not a way to cross the tenant boundary and normally resolves as not found.
Success and error handling
Success responses are JSON unless an endpoint explicitly returns a different representation, such as a PDF. Error responses use this common envelope:
{
"error": "Human-readable error or stable error token"
}Some errors add machine-readable context. For example, a permission denial includes required_permission and granted_permissions; validation endpoints can add errors and warnings arrays. Copilot handlers use a structured error object with code and message; inspect the endpoint's envelope instead of assuming that every error is a string. A 204 response has no body, and a successful PDF response is binary.
| Status | Meaning | Client action |
|---|---|---|
400 | Malformed request or missing envelope | Fix serialization or required structure; do not retry unchanged. |
401 | Missing, invalid, or revoked API key | Stop and rotate or correct the key. |
403 | Key is valid but lacks access | Request the required scope; do not retry unchanged. |
404 | Route or tenant-scoped resource not found | Verify version, ID, company, and environment. |
409 | State or idempotency conflict | Read current state before deciding whether to retry. |
412 | A resource prerequisite is missing | Complete the prerequisite, such as adding a payment method. |
422 | Semantically invalid field or transition | Surface field/state feedback and correct the request. |
429 | Daily API-key allowance exceeded | Stop automatic retries until the rate-limit window resets. |
5xx | Temporary server-side failure | Retry only safe or idempotent operations with backoff. |
Log the request ID
Every response includes X-Request-Id. Persist it with method, route template, status, duration, and your own correlation ID. Share it with Donutwork support; never attach the bearer token or unredacted payload.
Rate limits
Authenticated responses expose the API-key allowance with:
X-RateLimit-Limit: configured daily request allowance;X-RateLimit-CurrentUsage: requests counted in the current window;X-RateLimit-Remaining: requests left;X-RateLimit-Reset: Unix timestamp for the reset boundary.
Throttle before Remaining reaches zero. A 429 response is not evidence that a write failed safely; reconcile the target resource before resubmitting.
Retries and idempotency
Retry GET requests after network failures and selected 5xx responses with exponential backoff and jitter. Cap attempts and total elapsed time. Do not retry 400, 401, 403, 404, or 422 unchanged.
Use the idempotency mechanism documented for the specific operation. Some mutations use an Idempotency-Key header; subscription lifecycle actions use a body key, external charge imports use charge.source + charge.external_id, and audit events use source + event_id. Do not assume a generic header provides replay protection. Reuse a key only for the same logical request and payload, and store it with your local operation until reconciliation completes. Reusing a payload-bound key with different content returns 409.
When an endpoint does not advertise idempotency, a timeout after sending the body is ambiguous. Read the resource or search by your stable external identifier before creating it again.
Pagination
List endpoints generally accept page starting at 1 and size with an endpoint-specific maximum, commonly 100. Responses commonly include count, per_page or size, page metadata, and elements.
Do not assume all collections use identical envelope names. Follow the schema shown on each endpoint and stop when the current page reaches the reported maximum or returns no elements.
Production launch checklist
- Use a dedicated production key with only the documented read/write scopes.
- Keep the key in a managed server-side secret store and verify it never reaches browser bundles, mobile apps, logs, or analytics.
- Set connection and total request timeouts; bound concurrency and retry budgets.
- Persist Donutwork resource IDs plus your stable external IDs.
- Record
X-Request-Id, HTTP status, and latency without sensitive bodies. - Reconcile asynchronous payment, workflow, email, and scan operations instead of treating acceptance as completion.
- Register and monitor webhooks for lifecycle events your integration depends on.
- Exercise key rotation and incident revocation before launch.
- Validate your integration against the machine-readable OpenAPI document.
Machine-readable references
- OpenAPI 3 document for tooling and client generation.
- Complete AI-readable reference for retrieval systems.
- Compact LLM index for documentation discovery.
- Security policy for responsible disclosure.