Daily caps, schedules, warm-up, sender health, reply-stop and blacklists are enforced in the database. The API cannot send more than the app can, and there is no flag to turn a cap off. Nothing is deleted through the API either: suppressing a lead or exiting an enrollment keeps the lead, its timeline and its chats.
Keys
Create them under Settings → API & webhooks → API keys (Managers). A key is shown once — we store only its hash, so a lost key cannot be recovered. Revoke it and make a new one. A workspace can have 25 active keys.X-API-Key: ok_live_… works too, for tools that can’t set an Authorization header.
A key acts as the member who created it, and audit entries name that member. If they leave the workspace or their role is lowered, the key loses the same rights immediately.
GET /v1/me returns the key’s role and scope, plus the ids you need elsewhere: clients, stages, tags and lists.
Routes
Building and publishing sequences, connecting senders and changing caps are not in the API. Do those in the app or through Claude.
Rate limits
Per key, per hour:
Responses carry
X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. A 429 carries Retry-After.
These protect the API — they are not your sending limits. For bulk work, send many ids in one call (lead_ids takes up to 10,000 on enrollments, 5,000 on enrich) rather than one call per lead.
Idempotency
Send anIdempotency-Key header on any write:
- Same key, same request → the first answer again, with
Idempotent-Replayed: true. Nothing runs twice. - Same key, different request →
409 E_IDEMPOTENCY_MISMATCH. - Same key while the first is still running →
409 E_IN_PROGRESS. - Answers below 500 are stored, including 4xx. A 5xx is not, because a failed call changed nothing — retry with the same key.
Partial updates
POST /v1/leads and PATCH /v1/leads/{id} change only the fields you send.
- A field you leave out is never blanked. An empty string or
nullcounts as left out. custommerges key by key.email_work,email_personal,public_identifierandclient_idare filled only when empty and never overwritten. Send a different value and the call succeeds, the old value stays, and the field appears inunchanged_fields.- Unknown fields are rejected with 422, so a typo can’t be silently dropped.
POST /v1/leads matches on LinkedIn id, then provider id, then work email, then personal email. No match creates the lead (201), a match updates it (200).
Preview, then commit
Enrolling takes two steps, the same as in the app:POST /v1/enrollments/preview— changes nothing. Returnseligible_ids, who is excluded and why, which sender each lead would get, warnings and a duration estimate.POST /v1/enrollmentswith the same sequence, thelead_idsyou want and"confirm": true.
Errors
remedy. The full reference, with every route and schema, is in the OpenAPI file — import it into Postman, Insomnia or Swagger UI.