API Guide
Idempotency
Safe retries for POST, PATCH, and DELETE using Idempotency-Key and If-Match.
Idempotency-Key — required on every write
Idempotency-Key is required on every POST, PATCH, and DELETE. A write without one is rejected with 400 idempotency_key_required before anything executes. This is what makes a retry after a timeout safe — it can never create a duplicate.
curl -X POST https://api.moveitpro.com/v1/leads \
-H "Authorization: Bearer mip_live_..." \
-H "Idempotency-Key: 6f6c2c1e-8f4b-4f1a-9c33-6a3e0d7a2b91" \
-H "Content-Type: application/json" \
-d '{"first_name":"Dana","last_name":"Reyes","email":"dana@example.com"}'
The key is 1–255 visible ASCII characters. A UUIDv4 is recommended but not required; anything outside that shape is 400 invalid_idempotency_key.
Generate one key per logical operation and reuse it for every retry of that operation.
- Replaying the same key with the same request returns the original result instead of writing again, marked with an
Idempotency-Replayed: trueresponse header. - Replaying the same key with a different payload is an error:
422 idempotency_key_reuse. - A same-key retry that arrives while the first attempt is still executing is
409 request_in_flightwith aRetry-Afterheader — wait and retry the same key.
Concurrency: If-Match
Updates and deletes use optimistic concurrency and require an If-Match header (428 precondition_required without one). The version token is the record's updated_at value, exactly as a GET returns it — an opaque timestamp, not a version number. Quotes around it are optional.
curl -X PATCH https://api.moveitpro.com/v1/customers/{id} \
-H "Authorization: Bearer mip_live_..." \
-H 'If-Match: "2026-08-01T14:22:05.123456+00:00"' \
-H "Idempotency-Key: …" \
-H "Content-Type: application/json" \
-d '{"email":"new@example.com"}'
Read the resource, take data.updated_at, send it back. On success the response body carries the new updated_at for your next write. Pass the timestamp through untouched — comparison is strict equality — and do not send an ETag here: the read ETag is a different value.
428 precondition_required— the route needsIf-Matchand none was sent.400 invalid_precondition— the header could not be used: weak validators (W/"…"),*, comma-lists, and non-timestamp values are all rejected.409 version_conflict— someone else changed the record. The error body includescurrent_version, the record's currentupdated_at— re-read (or usecurrent_versiondirectly after re-checking the record), re-apply, retry.409 request_in_flight— an identical idempotent request is still executing. Wait and retry the same key.
Recommended retry loop
- Generate one
Idempotency-Keyper logical write and send it on every attempt. - Retry on
429(honorRetry-After),500,503, network timeouts, andrequest_in_flight— always with the same key. - On
502 write_indeterminate, do not retry with the same key: the indeterminate result is stored against it and will simply replay.GETthe record first; if the write didn't land, retry with a new key. - On
409 version_conflict, re-read, re-apply, and retry with a freshIf-Match. - Never retry
400,401,403,404, or422.