> ## Documentation Index
> Fetch the complete documentation index at: https://developer.firstsales.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency

> Retry write requests without duplicating side effects.

For retryable mutations, send an idempotency key.

```bash theme={null}
curl "$FIRSTSALES_BASE_URL/api/v1/organizations/org_123/workspaces/ws_123/contacts" \
  -X POST \
  -H "Authorization: Bearer $FIRSTSALES_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: import-row-001" \
  -d '{"email":"alex@example.com"}'
```

The CLI exposes the same header:

```bash theme={null}
firstsales contacts create \
  --org org_123 \
  --workspace ws_123 \
  --data '{"email":"alex@example.com"}' \
  --idempotency-key import-row-001
```

## Rules

* Keys are stored per organization and Developer API key for 24 hours.
* The stored record covers the method, the route, and the request body.
* Reusing a key with the same method, route, and body returns the original response.
* Reusing a key with a different method, route, or body returns `409 idempotency_key_reused`.
* Sending the key again while the first request is still running returns
  `409 idempotency_request_in_progress`. Retry the same request after a short wait.
* Use deterministic keys for imports, approvals, and lifecycle actions.
* Do not use the same idempotency key across unrelated operations.

## Routes that require a key

These writes return `400 idempotency_key_required` when the `Idempotency-Key` header
is missing or blank:

| Operation | Route |
| - | - |
| Update offering | `PATCH /api/v1/organizations/{orgId}/workspaces/{wsId}/offerings/{offeringId}` |
| Approve inbox draft | `POST /api/v1/organizations/{orgId}/workspaces/{wsId}/inbox/drafts/{emailId}/approve` |
| Reply to inbox thread | `POST /api/v1/organizations/{orgId}/workspaces/{wsId}/inbox/threads/{threadId}/reply` |
| Create billing checkout | `POST /api/v1/organizations/{orgId}/billing/checkout` |
| Purchase credit top-up | `POST /api/v1/organizations/{orgId}/billing/top-up` |

On other writes the key is optional but recommended.

## Replays and rate limits

An exact replay does not spend rate-limit budget. The per-key limit is skipped only
when all of these hold:

* the request is a `POST`, `PUT`, `PATCH`, or `DELETE` under `/api/v1`;
* a completed record exists for the same organization, API key, and idempotency key;
* the method, the concrete route, and the body match that record.

Any other request with a key, including a reuse on another route or body, is metered
normally. `GET` requests never skip the limit.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.