Capabilities
What each line can do, why a command can be accepted and then fail as unsupported, and how to tell the capability failure codes apart.
Feature-detect on GET /v1/whoami
lines[].capabilities on GET /v1/whoami lists what each of your lines can do —
the command types it will carry, plus platform facts such as handle_check and receipts — and
it is the list to branch on. A command can still be refused by the line after a 202, so keep the
code that handles the failure below.
What failure looks like
The capability check happens on the way to the line, not at the API boundary.
So a command requesting something your line cannot do still returns 202:
{ "commandId": "cmd-1", "status": "queued", "disposition": "queued" }and then settles as failed on the
response stream with a capability
errorCode:
{
"commandId": "cmd-1",
"logicalActionId": "order-8842-confirmation",
"status": "failed",
"errorCode": "capability_not_negotiated",
"conversationId": "a0000000-0000-4000-8000-000000000001",
"failureClass": "capability"
}Telling the codes apart
capability_not_negotiated — the line cannot do this operation at all.
Retrying is pointless; the identical request will fail identically until the
line is updated. Do something the line can instead: a short text reply in place of a tapback, or a
one-to-one message to each person in place of a group. A chat_background.set
or a chat_background.remove settles this way on a line that cannot set, or
cannot remove, a background — a line can carry one without the other.
capability_constraint_violation — the line can do the operation, but not
with a parameter in your request: a tapback's remove: true, for example, or a
reaction outside the set the line declares, or a group.update that adds or
removes a member on a line that cannot change a group's members. The operation is supported; this variant of it
is not. Change the request, not the timing.
invalid_session_capabilities — a fault on the line's side rather than
in your command. Treat it as transient, back off, and escalate if it persists.
unsupported_capability — the line declined the operation when it ran it,
after the 202. Handle it the same way as
capability_not_negotiated: do not retry unchanged.
None of the four is visible at request time — the 202 looks the same in every case — which is
the practical argument for building a fallback path rather than probing. When a tapback,
name_photo.share or group.create settles with a capability code, say it in plain text instead;
a message.send of text alone needs no content capability, so the reader gets the message either
way.
Related
Idempotency and retries
Send the same Idempotency-Key again and the first command is adopted rather than repeated — on every command in test, and on every live command but group.create, typing.start and message.read. Leave the header off and every attempt is a new command.
Handling errors
Errors arrive in two places, and a client that only checks HTTP responses will believe every message was delivered.