Getting started
MailKey turns a short key a person gives you into their exact name and current mailing address, with their consent. While they let you keep it, MailKey keeps it current: when they move, the grant you hold shows the new address.
A key is 10 characters, read in three groups, such as K7M-XQQ-D9RA. People find theirs in the
MailKey app and read it to you over the phone, type it into a form or hand it to an assistant.
What your business gets
Section titled “What your business gets”- The name exactly as typed. Names come back byte for byte as the person entered them, capitals, spaces, accents and all: “Zoë van der Berg”, never “ZOE VAN DER BERG”. Print them as they come.
- A standardized mailing address. Every address is standardized by USPS before it is shared,
in upper case, with ZIP+4, plus ready-made
label_linesfor an envelope. - The address stays current. Read the grant whenever you mail something. A scheduled move
shows up as
upcoming_addressbefore it takes effect, and webhooks tell you when anything changes. - Consent on record. The person sees every business holding their address in the app, and can revoke a grant at any time.
Get an API key
Section titled “Get an API key”- Sign in to the business console: app.mkey.ai/business in production, staging.mkey.ai/business on staging.
- Your business must be in production. A new business starts in demo mode, which works only in
the console; every API call from a demo business answers
403 business_not_in_production. Apply for production from the console. MailKey reviews each application by hand during the beta. - Open API keys, name a key (for example after the system that will use it) and create it. Only an owner of the business can create or revoke keys.
- Copy the secret,
mk_live_followed by 32 characters. It is shown once; MailKey stores only a hash of it. Keep it on your server, never in a browser or mobile app.
Authenticate
Section titled “Authenticate”Send the key on every request in the Authorization header:
GET /v1/status HTTP/1.1Host: api.mkey.aiAuthorization: Bearer mk_live_...GET /v1/status is never counted against your limits, so it is a safe first call to check a key:
curl https://api.mkey.ai/v1/status \ --header "Authorization: Bearer $MAILKEY_API_KEY"A missing, unknown or revoked key answers 401 unauthorized.
Preview, read back, claim
Section titled “Preview, read back, claim”Every lookup has three steps. The read-back in the middle is the safeguard that catches a misheard or mistyped key before any personal data reaches you.
1. Preview
Section titled “1. Preview”POST /v1/previews with the key the person gave you.
curl https://api.mkey.ai/v1/previews \ --header "Authorization: Bearer $MAILKEY_API_KEY" \ --header "Content-Type: application/json" \ --data '{"key": "K7M-XQQ-D9RA"}'The answer is masked: the given name, the family initial and the city.
{ "preview_id": "0199a3b2-1f4e-7a6b-9c8d-2e3f4a5b6c7d", "display": { "name": "Zoë v.", "locality": "WASHINGTON, DC" }, "expires_at": "2026-10-01T15:14:05.000Z"}A key that is not well formed answers 400 invalid_key_format and is not counted. A key that
matches no one answers 404 key_not_found. A child’s key answers exactly the same way, so a
business can never tell that a child holds it.
2. Read it back
Section titled “2. Read it back”Read the preview to the person, “Zoë v. in Washington, DC?”, and wait for them to confirm it is them. Claim only after a clear yes. If they say no, ask for the key again; do not claim a preview the person did not confirm. On a form, show the masked preview and ask the person to confirm it the same way.
3. Claim
Section titled “3. Claim”POST /v1/grants with the preview_id, before expires_at. Add your own external_ref, such as
a customer number, to find the grant later.
curl https://api.mkey.ai/v1/grants \ --header "Authorization: Bearer $MAILKEY_API_KEY" \ --header "Content-Type: application/json" \ --data '{"preview_id": "0199a3b2-1f4e-7a6b-9c8d-2e3f4a5b6c7d", "external_ref": "CUST-1042"}'Most claims answer 201 with an active grant:
{ "id": "0199a3b2-7d8e-7f90-a1b2-c3d4e5f6a7b8", "status": "active", "external_ref": "CUST-1042", "claimed_at": "2026-10-01T15:05:12.000Z", "name": { "given_name": "Zoë", "middle_name": null, "family_name": "van der Berg", "suffix": null, "mailing_name": null }, "address": { "line1": "1600 PENNSYLVANIA AVE NW", "line2": "", "city": "WASHINGTON", "state": "DC", "zip5": "20500", "zip4": "0005", "label_lines": ["Zoë van der Berg", "1600 PENNSYLVANIA AVE NW", "WASHINGTON DC 20500-0005"] }, "confirmed_at": "2026-09-14T18:30:00.000Z", "stale": false}Some people approve each business claim in the MailKey app first. Then the claim answers 202
with "status": "pending_approval", and a grant.approved or grant.denied webhook follows
when they answer.
The claim window is set per business in the console: 10 minutes by default, 60 at most. After it,
the claim answers 410 claim_window_expired; look the key up again and read it back again. Only
the business that made a preview can claim it, and claiming the same preview twice returns the
same grant.
Afterwards
Section titled “Afterwards”GET /v1/grants/{id}whenever you mail something. Read it rather than keeping your own copy, because the address changes when the person moves.- A grant the person revoked answers
410 grant_revoked. Delete the name and address you hold. DELETE /v1/grants/{id}when you no longer need the address. The business terms require you to delete what you hold once you release it.POST /v1/grants/{id}/returned-mailwhen mail comes back. MailKey asks the person to confirm or update their address.
Call ids for voice agents
Section titled “Call ids for voice agents”The voice-agent routes under /v1/voice, and the same tools over MCP, take the phone
call’s id: the X-Call-Id header, or a call_id field when the platform cannot set a header. The
header wins when both are sent. It is 1 to 128 printable characters without spaces; most platforms
have a conversation id that fits.
- A preview belongs to the call that made it. Claiming it from another call answers
409 preview_other_call. - Each call gets 3 lookups, on top of your business’s limits. The fourth answers
429 call_preview_limit. A key that is not well formed does not count. - Each answer carries a
saysentence for the agent to speak, such as “Zoë v. in Washington, District of Columbia”, and the person’s access history shows the claim came through your voice agent.
Errors
Section titled “Errors”Every error has the same shape, and every response carries the same id in the X-Request-Id
header. Quote the request_id when you contact MailKey about a call.
{ "error": { "code": "claim_window_expired", "message": "The claim window for that preview has ended; look the key up again.", "request_id": "0199a5e1-4c2b-7d3e-8f4a-5b6c7d8e9f01" }}Branch on code, not on message; messages may be reworded. Each operation in the
API reference lists the codes it can return.
| Status | Meaning |
|---|---|
400 |
The request or one of its fields is invalid. |
401 |
The API key is missing, unknown or revoked. |
403 |
The business is not in production yet. |
404 |
Nothing of yours has that id, or no key matches. |
409 |
The grant, endpoint or call does not allow this. |
410 |
The claim window ended, the key is retired, or the person revoked the grant. |
429 |
Rate limited; wait the number of seconds in Retry-After. |
503 |
MailKey cannot read person data right now; retry shortly. |
Rate limits
Section titled “Rate limits”| Limit | Free plan |
|---|---|
| Requests | 60 a minute per business |
| Previews | 2,000 a day per business |
| Unknown keys | 20 404 answers within 10 minutes drop the business to 1 request a minute for an hour, and its admins are told |
| One person’s keys | 10 previews an hour across every business; past that the key answers 404 like an unknown key |
| Transcript decoding | 60 a minute per API key, separate from the request limit |
| Voice calls | 3 lookups per call |
Claims need no cap of their own, because each claim needs a preview. GET /v1/status shows your
plan’s limits and today’s usage and is never counted. Over a limit, the answer is
429 rate_limited with a Retry-After header.
Staging and production
Section titled “Staging and production”| Production | Staging | |
|---|---|---|
| API | https://api.mkey.ai |
https://api-staging.mkey.ai |
| Business console | https://app.mkey.ai/business |
https://staging.mkey.ai/business |
| MCP for voice agents | https://mcp.mkey.ai/business |
https://mcp-staging.mkey.ai/business |
| MCP for personal assistants | https://mcp.mkey.ai/mcp |
https://mcp-staging.mkey.ai/mcp |
| These docs | https://developers.mkey.ai |
https://developers-staging.mkey.ai |
The two are separate: staging has its own businesses, API keys and test people, and a key from one does not work on the other. Build and test against staging, then create a production key. Each docs site’s Try it panel calls its own environment’s API.