Authentication

How requests are identified, and how to keep your credential safe.

Every request to the Mapier API carries an API key in the Authorization header.

curl https://api.mapier.ai/v1/handle-check \
  -H "Authorization: Bearer $MAPIER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "address": "+14155550100" }'

A request without one — or with a key that has been revoked or has expired — is rejected before anything else happens:

401 Unauthorized
{ "error": { "code": "invalid_credentials", "message": "That API key is not valid." } }

What a key looks like

A key is a prefix and 43 characters of base62:

mp_live_9Kq2…   a live key — reaches a real line and real handsets
mp_test_Xa7v…   a test key — reaches your project's sandbox and nothing else

The prefix is the only readable part, and it is deliberate: a key's environment is in its name, so a test key pasted into a production deploy fails loudly rather than texting a customer, and a live key in a test harness is visible in the first log line. The two environments share no data — test mode says what a test key can and cannot reach.

Where keys come from

Keys are issued in the console, per project and per environment: Keys → Create key. The plaintext is shown exactly once, in the dialog that creates it, and never again — only a SHA-256 hash is stored, so there is nothing to look up later. Copy it into your secret manager before you close the dialog.

Each key carries a name, an optional expiry, and a last-used stamp the console shows beside it, so an unfamiliar key that is still being used is one you can see. A key is revoked from the same screen; a revoked key answers 401 on its next request, whatever it was doing.

What a key identifies

A key maps to one project in one environment, and a live project maps to the lines it holds — the real Apple accounts on real Macs that actually talk to iMessage. One key sends from every line its project holds. You do not have to choose: a person is answered on the number they last wrote to, and somebody new gets the line the platform picks. When you do want to choose, name the line at target.lineId — see Choosing the line.

Three consequences worth designing around:

  • Every resource is project-scoped. A conversation id or attachment id from another project returns 404, indistinguishable from one that does not exist. Existence is never revealed across projects.
  • A key is fully privileged within its project and environment. A key that can read the stream can also send messages. There is no per-key scoping.
  • Keys share the project's command budget. Every key of a project spends one command ceiling per environment, so a second key is not more headroom. See Rate limits.

Handling the key

Store it outside your code

Put it in an environment variable or a secret manager. Never commit it, and never ship it to a browser — the API is server-to-server, and there is no public or publishable key. The console's own quickstart prints MAPIER_API_KEY=<paste-your-key-here> rather than a value for the same reason.

export MAPIER_API_KEY="mp_test_…"

Read it once at startup

const MAPIER_API_KEY = process.env.MAPIER_API_KEY;
if (!MAPIER_API_KEY) throw new Error("MAPIER_API_KEY is not set");

Failing loudly at boot beats discovering it in a 401 under load.

Keep it out of logs

The key travels in a header, so anything that dumps request headers leaks it. Redact Authorization in your logging middleware, and be careful with verbose HTTP clients — curl -v prints the header in full.

A small client

Everything in these docs is plain HTTP, so a wrapper is a few lines:

const BASE = "https://api.mapier.ai";

export async function mapier(path: string, init: RequestInit = {}) {
  const res = await fetch(`${BASE}${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.MAPIER_API_KEY}`,
      "Content-Type": "application/json",
      ...init.headers,
    },
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({}));
    throw new Error(`${res.status} ${body.error?.code ?? "request failed"}`);
  }
  return res;
}

The full surface, with every request and response shape, is the OpenAPI document at https://api.mapier.ai/openapi.json — /v1 only, which is the whole of what a key can reach.

On this page