Conventions
ReferenceBase URL and versioning
Every request goes to https://app.envoycrm.com/api/v1/... (a self-hosted install uses its own domain instead). v1 is part of the path from day one, even though there's only one version today - a future breaking change would ship as /api/v2 rather than changing what /api/v1 means underneath you.
Resources
contacts, companies, deals, activities, and notes support GET (list and by id), POST, PATCH, and DELETE. brands is read-only (GET only) - brand management stays an in-app concern; the API only needs brand ids to scope the other resources.
Pagination
List endpoints accept page and limit query parameters (default page=1, limit=50, maximum limit=200) and return:
{ "items": [ ... ], "total": 137 }Errors
A consistent JSON shape across every endpoint:
{ "error": "A human-readable message", "code": "optional_machine_readable_code" }Prefer branching on code when it's present, rather than matching against the text of error, which can change. A request that fails Zod's schema validation (a missing required field, a value of the wrong type) returns 400 with code: "FST_ERR_VALIDATION" before your request reaches any application logic at all.
Idempotency
POST requests accept an optional Idempotency-Key header. If a request is retried with the same key within 24 hours, the original response is returned unchanged rather than creating a second resource - useful for retrying a request after a dropped connection without risking a duplicate.
curl -X POST https://app.envoycrm.com/api/v1/contacts \
-H "Authorization: Bearer envoy_live_..." \
-H "Idempotency-Key: a-uuid-you-generate-per-logical-request" \
-H "Content-Type: application/json" \
-d '{ "firstName": "Jane" }'Reusing the same Idempotency-Key for a genuinely different request (a different endpoint, or a different method) returns 422 rather than silently replaying the wrong response - generate a new key per logical operation, the same way you would with Stripe's API.
Rate limits
Limits apply per API key, not per IP address, and reset every minute. The current limit and remaining budget are returned on every response:
x-ratelimit-limit: 300
x-ratelimit-remaining: 297
x-ratelimit-reset: 60Exceeding the limit returns 429. Limits scale with your plan:
| Plan | Requests / minute |
|---|---|
| Starter | 120 |
| Growth (and trial) | 300 |
| Agency | 600 |
Next
- API Reference - every endpoint, with a live try-it console