Get started
Authentication
API keys, scopes and who may grant them, rep restriction, IP allowlists, rotation, expiry and leaked keys.
Every request carries an API key. A key belongs to one workspace, acts as the person who created it, and can do only what its scopes allow and what that person's role still allows.
Sending the key
Either header works. Use one, not both:
curl https://api.dmhero.ai/v1/me -H "Authorization: Bearer $DMHERO_API_KEY"
curl https://api.dmhero.ai/v1/me -H "X-API-Key: $DMHERO_API_KEY"A key looks like dmh_live_ followed by a 12 character prefix, an underscore and a 40 character secret. The prefix is not secret: it is shown in the app and in logs so you can tell keys apart. The secret is shown once, when the key is created; DM Hero stores only a hash of the whole key.
A missing, malformed, unknown, revoked or expired key, and a request from outside the key's IP allowlist, all get the same answer: 401 API_KEY_INVALID_401. The key's request log in the app tells its owner which one it was.
Server side only
Keep keys on servers, in environment variables or a secret manager. The API sends no CORS headers, so a browser page cannot call it with a key; that is deliberate, because a key in browser JavaScript is readable by anyone who opens the page.
Scopes
A key holds a list of scopes, and every operation names the scopes it needs (all of them). A missing scope answers 403 API_SCOPE_MISSING_403. Aliases: campaigns:enroll holds enrollments:write.
Who may grant a scope is checked twice: when the key is created, and again on every request against the creator's current role. Demote the creator and the key loses the scopes the new role cannot grant, within seconds, without anyone touching the key.
conversations:read and conversations:state can only be granted by the organization owner: such a key reaches every thread of its reps.Developer keys and Clay compatible keys
Keys created before the developer API, and keys created with the Clay compatible preset, can call only the five original endpoints (GET /v1/me, GET /v1/campaigns, POST /v1/leads, GET /v1/leads/lookup, POST /v1/campaign-leads). Anything else answers 403 API_KEY_SURFACE_403. Create a developer key for the rest of the API.
Restricting a key to reps
A key can be limited to some of the workspace's reps (connected Instagram accounts). Such a key sees and acts on only their conversations, enrollments, setter modes, rep analytics and webhook endpoints filtered to them; another rep's resource answers 404 as if it did not exist. Workspace data (leads, templates, campaigns, tags, folders, custom fields, discovery) is shared by every key of the workspace.
IP allowlist
Where your organization has it available, a key can carry up to 20 IPv4 or IPv6 ranges in CIDR form. A request from any other address answers the same 401 as a wrong key, so whoever holds a stolen key learns nothing, and the owner's request log shows "IP not on the allowlist".
Expiry and rotation
- A key expires after 30, 90, 365 days, or never; you choose when you create it. The app warns two weeks ahead.
- Rotate to replace a key without downtime: the new key copies the old one's name, scopes, reps and IP ranges, and the old key keeps working for a grace period of 1 hour, 24 hours, 7 days (24 hours by default). Deploy the new key, then let the old one lapse.
- Only the key's creator or the owner may rotate it, and whoever rotates becomes the new key's creator, so the role checks above run against them.
- An
Idempotency-Keyused with the old key still deduplicates with the new one. - A workspace holds at most 10 active keys.
Revoking
Revoke a key in the app and it stops working everywhere within about five seconds. A member may revoke the keys they created; an owner or an admin may revoke any key of the workspace. A revoked key's queued jobs fail with JOB_KEY_REVOKED at their next step.
Revoking a key also switches off the webhook endpoints it created through the API, together with the ones created by the keys it was rotated from, and cancels their queued deliveries. They are not deleted: they show is_active: false with disabled_reason: "key_revoked", and an owner can switch them back on in the app, or another live key with webhooks:manage through the API (the endpoint then counts as created by that key). Rotating a key leaves them on, and so does revoking the old key once its replacement exists, since that only ends the grace period early. A key found leaked switches them off even then.
Leaked keys
dmh_live_ key pushed to a public GitHub repository is reported to us and revoked automatically: we cancel the jobs and discovery runs it started, switch off the webhook endpoints it created and mark it in the key list as "Revoked: found public on GitHub" with the link to where it was found. Until then, nothing revokes a leaked key for you.