Versions

The x-mapier-version header every /v1 route reads, the version your project gets when you omit it, and what each version changes.

The major version is in the path — /v1 — and a /v2 would sit beside it rather than replacing it. Within /v1, behaviour that a deployed integration could trip over changes only behind a dated version, which you choose with one header:

x-mapier-version: 2026-10-15

Every /v1 route reads it. A version is a date, and a date is a promise: it is the first day a project that mints its first API key defaults to that behaviour, and nothing changes under an integration that keeps sending it.

The versions

VersionWhat it changes
2026-09-01The oldest. What every project that had a key before any later version gets when it sends no header.
2026-10-01A command target that names no channel is resolved from your project's lines rather than meaning iMessage.
2026-10-15Refusals speak the transport-neutral vocabulary: one code and three line_unavailable reasons change their word. The newest version.

Fields and frames are added without a version, and a new refusal can join a status an operation already documents without one — service_busy joined every operation's 503 that way — so parse defensively, as the API reference says. A version is spent where an integration already deployed would otherwise be handed a different word for the same answer, as 2026-10-15 is, or have the same request resolved differently, as 2026-10-01 is.

When you send no header

You get your project's default: the newest version dated on or before the day the project's first API key was created — test or live, revoked or not. So:

  • Rotating a key changes nothing, and neither does moving from a test key to a live one: both are keys of the same project, and the project's first key is where the default comes from.
  • A project whose first key came after a change has that change without asking. A new project defaults to whatever is newest on the day of its first key.
  • Moving an integration to a new project can change its behaviour. An integration started on a project that already had keys gets that project's older default, and the same integration pointed at a new project gets the new one's.

Pin a version when you start, and before you point an existing integration at a new project. Pinned, nothing under you moves until you change the header yourself.

What each version changes

2026-10-01 — the channel of a target

On POST /v1/commands/{type} a target may name the channel it is on, target.channel. Below 2026-10-01 a target that names none means iMessage. From 2026-10-01 it is resolved from your project's lines, in this order:

  • Active lines decide first. When every active line is on one channel, the target means that channel — a paused line on another channel beside active iMessage ones does not make an iMessage send ambiguous.
  • With no active line, the lines the project still holds decide — every status but released, so a paused or provisioning line counts and a released one never does. Held lines on one channel mean that channel, and the send is then answered by that line's state: 503 line_unavailable with details.reason: "line_paused" for a contact pinned to a paused line, and 503 no_line for a contact not yet pinned to any.
  • A project that holds no line at all answers 503 no_line, before the recipient's address is read.
  • Lines on more than one channel — active ones, or held ones when none is active — answer 422 validation_failed at target.channel, listing those channels in the entry's channels. Name one of them.
POST /v1/commands/message.send, x-mapier-version: 2026-10-01 — 422
{
  "error": {
    "code": "validation_failed",
    "message": "This project has lines on more than one channel.",
    "details": [
      {
        "path": "target.channel",
        "message": "Name the channel to send on: …",
        "channels": ["imessage", "…"]
      }
    ]
  }
}

Name the channel whenever a target names an address — "target": { "channel": "imessage", … } — and this refusal can never reach you, whatever lines the project comes to hold. A conversation target is on its thread's channel and is never refused this way. No other operation reads a target, so nothing else differs at this version.

2026-10-15 — the transport-neutral words

The same refusals, the same statuses and the same remedies — only the word changes. You receive one word of each pair, never both:

Below 2026-10-15At 2026-10-15 or laterWhere
no_line_capacitycapacity_exhaustedthe 503 code, on POST /v1/commands/{type}
relay_downtransport_downa 503 line_unavailable reason
relay_unconfiguredtransport_unconfigureda 503 line_unavailable reason
line_releasedline_retireda 503 line_unavailable reason

The code pair is answered by POST /v1/commands/{type} alone. The reason pairs are answered wherever line_unavailable is: sending a command, handle check, attachments and receipts. A client that switches on both words of each pair works at every version; one that switches on the older word alone breaks the day its project, or its pin, reaches 2026-10-15.

The operations where nothing differs

GET /v1/response_stream and GET /v1/whoami behave the same at every version listed. They read the header all the same, so a value they do not know is refused there too.

A version this build does not know

Any other value is 422 validation_failed, whose entry lists every version the build answers to in supported:

x-mapier-version: 2026-13-01 — 422
{
  "error": {
    "code": "validation_failed",
    "message": "That is not an API version this build answers to.",
    "details": [
      {
        "path": "x-mapier-version",
        "message": "Send one of: 2026-09-01, 2026-10-01, 2026-10-15. Omit the header to keep the behaviour your integration was written against.",
        "supported": ["2026-09-01", "2026-10-01", "2026-10-15"]
      }
    ]
  }
}

The OpenAPI document lists the same versions as the header's enum on every operation, with a sentence per operation on what differs.

On this page