API Changelog
Notable changes to the public ArelHub API contract. Breaking changes are highlighted explicitly.
Policy
- Breaking changes require a minimum 60-day deprecation notice before removal.
- Non-breaking additions (new optional fields, new endpoints) do not require a deprecation period.
- Staff, internal, and deferred endpoints are excluded from this changelog.
A changelog entry is mandatory when a public route path changes, a required field is added or removed, a response field integrators rely on is renamed, a status code changes meaning, or auth scheme requirements change.
2026-10-18 — Recipient consent
Additive, with one behaviour change for STOP replies
Added
- Account routes under
v1/accounts/{accountId}/recipient-consent(list, preferences, opt-out, opt-in) for account-level sending. An account opt-out takes a requiredscopeofaccountLevelOnlyorentireAccount. - CSV import and export of opt-outs, and storage, listing, import and export of opt-in evidence
(
opt-in-records), on both the account and the subaccount routes. - API-key rights
ReadRecipientConsentandManageRecipientConsent. Existing keys do not have them. The subaccount consent routes now accept API keys with these rights, and subaccount users. - Webhook events
RecipientOptedOutandRecipientOptedIn. They are separate subscriptions: existing endpoints do not receive them until you add them tosubscribedEvents. - Error codes
RECIPIENT_OPT_OUT_SCOPE_REQUIRED,RECIPIENT_NOT_OPTED_OUT,RECIPIENT_OPT_OUT_NOT_LIFTABLEandCONSENT_IMPORT_INVALID. scopeandsubaccountIdon each opt-out in the list responses.
Changed
- A
STOPreply now applies to the number that received it: the subaccount that owns the number, or the whole account for an account-level number. Before, it blocked the whole account. - A recipient who replied
STOPis blocked on every channel attempt of a message, including a fallback channel. - A subaccount route you cannot reach answers
404instead of403. Re-subscribe refusals useRECIPIENT_OPT_OUT_NOT_LIFTABLEorRECIPIENT_NOT_OPTED_OUTinstead ofCONFLICT. GET v1/accounts/{accountId}/inbound-messagesis no longer available to an API key scoped to a subaccount.
2026-10-16 — Delivery-receipt fallback
Additive
Added
receiptTimeoutSeconds(60 to 86400;0turns it off) andfallbackOnDeliveryFailureonPUT /v1/subaccounts/{subaccountId}/routing-policy, and on the policy resource. A message that is sent but not confirmed within the timeout, or whose delivery fails, is sent on the next channel of the routing policy. Leave a field out to keep it.
Notes for integrators
- The first message is not recalled, so the recipient may receive it on both channels. Both settings are off by default.
- Sending a message (
POST /v1/accounts/{accountId}/messagesandPOST /v1/subaccounts/{subaccountId}/messages) now checks the account's status, subscription and quota, as documented. A send from an account without an active subscription returns402 NO_ACTIVE_SUBSCRIPTION, and a send over quota returns429. These checks were not being applied to these routes before. Every accepted send counts against the quota, a scheduled message when it is accepted.
2026-10-16 — Message priority
Additive
Added
priority(Normal, the default, orUrgent) onPOST /v1/accounts/{accountId}/messagesandPOST /v1/subaccounts/{subaccountId}/messages, and on the message resource. Urgent messages go ahead of normal ones whenever sending has to wait: retries, messages released from an account hold, and scheduled messages, which are sent as soon as theirsendAtarrives.
2026-10-16 — Message expiry
Additive
Added
expiresAtonPOST /v1/accounts/{accountId}/messagesandPOST /v1/subaccounts/{subaccountId}/messages: after this UTC time the message is no longer sent or charged. It is also returned on the message resource.- Message state
Expiredand webhook eventMessageExpired. replyStatusvalueExpired, and"afterExpiry": truein thereplyblock of aMessageReceivedwebhook for a reply that arrived after the message expired.- Error code
EXPIRES_AT_INVALID(400).
Notes for integrators
MessageExpiredis part of the "all message events" group. An endpoint subscribed to that group receives it; one that lists events by name does not until you add it.- Code that switches on the message state should treat
Expiredas final and not sent.
2026-10-16 — Scheduled sends
Additive
Added
sendAtonPOST /v1/accounts/{accountId}/messagesandPOST /v1/subaccounts/{subaccountId}/messages: send the message at a later UTC time, between 60 seconds and 30 days ahead.sendAtis also returned on the message resource.- Message states
Scheduled(waiting for itssendAt) andCanceled. POST /v1/accounts/{accountId}/messages/{messageId}/cancelandPOST /v1/subaccounts/{subaccountId}/messages/{messageId}/cancel: cancel a message that is stillScheduled.- Error codes
SEND_AT_OUT_OF_RANGE(400) andMESSAGE_NOT_CANCELABLE(409).
Notes for integrators
- Code that switches on the message state should treat
Scheduledas "not yet sent" andCanceledas final.
2026-10-15 — Reply options
Additive
Added
replyOptionsonPOST /v1/templatesandPUT /v1/templates/{templateId}, and on the template resource: the short answers a recipient may text back to a message sent from an SMS template (key,value,aliases). On update, omit it to keep the options, send[]to remove them. Up to 10 options with up to 5 aliases each; keys and aliases are 1 to 20 letters or digits and the wordsCANCEL,END,HELP,INFO,QUIT,START,STOP,STOPALL,UNSTOP,UNSUBSCRIBEandYESare reserved.replyStatus(None,Awaiting,Answered),replyOptionKey,replyOptionValueandansweredAton the message resource.replyOptionKeyandreplyOptionValueon each reply returned by the inbound-message and replies endpoints.data.reply({ "matched": true, "key", "value" }or{ "matched": false }) on theMessageReceivedwebhook, present when the message answered offered reply options.- Error codes
REPLY_OPTION_RESERVED_KEYWORD(2203) andREPLY_OPTION_INVALID(2204), returned as400when a template's reply options are refused.
Fixed
PUT /v1/templates/{templateId}no longer overwrites the template's channel whenchannelTypeis left out of the request.
2026-10-15 — Replies
Additive · One behaviour change to number pools
Added
clientReference(up to 128 characters) onPOST /v1/accounts/{accountId}/messagesandPOST /v1/subaccounts/{subaccountId}/messages. It is returned on the message, in thedataof every webhook for the message, and with every reply to it.- Replies from recipients are stored and linked to the message they answer:
GET /v1/accounts/{accountId}/inbound-messages,GET /v1/subaccounts/{subaccountId}/inbound-messages(newest first, paged withcursor/nextCursor),GET /v1/accounts/{accountId}/messages/{messageId}/repliesandGET /v1/subaccounts/{subaccountId}/messages/{messageId}/replies(oldest first). - Webhook event
MessageReceived, sent when a recipient replies. It is a separate subscription: it is not part of the message-status events, so existing endpoints do not receive it untilMessageReceivedis added to theirsubscribedEvents. fromAddresson each entry of a message'schannelHistory: the number that attempt was sent from.- Number pool strategy
Sticky("strategy": "Sticky"onPOST /v1/number-pools), which keeps a recipient on the number they last heard from.
Changed
- Number pools using
RoundRobinorLeastUsednow rotate across their numbers. Until now every send from a pool used its first active number unless the strategy wasRandom. Make sure every number in a pool is ready to send (for a long code, assigned to a campaign) before relying on rotation.
2026-10-06 — Passkeys
Additive · Off until an environment turns them on
Added
- Passkeys and security keys (WebAuthn) as an additional second factor for a user who already has MFA on:
GET /v1/auth/mfa/webauthn,POST /v1/auth/mfa/webauthn/register/optionsand/register/complete, andPATCH/DELETE /v1/auth/mfa/webauthn/{id}. Registration and removal need a step-up token forMfaManage. - Sign-in:
MFA_REQUIREDlistswebauthninmfaMethodswhen the user has a passkey. UsePOST /v1/auth/login/mfa/webauthn/options, thenPOST /v1/auth/login/mfawithmethod: "webauthn". - Step-up:
POST /v1/auth/mfa/step-up/webauthn/options, thenPOST /v1/auth/mfa/step-upwithmethod: "webauthn". hasPasskeysonGET /v1/auth/mfa, and error codeWEBAUTHN_VERIFICATION_FAILED.
Changed
- Turning MFA off, or a staff MFA reset, also removes the user's passkeys.
2026-10-06 — Account-required MFA
Additive · Applies only to accounts whose owner turns the policy on
Added
GETandPUT /v1/accounts/{accountId}/security-policy: an account owner (Admin) can require MFA for every user on the account and its subaccounts.PUTneeds a step-up token forAccountSecurityPolicy, and the Admin must have MFA on first.401 MFA_ENROLLMENT_REQUIRED(withenrollmentTokenandenrollmentTokenExpiresAt) fromPOST /v1/auth/loginandPOST /v1/auth/external-loginfor a user without MFA on a policy account.POST /v1/auth/login/mfa/enroll/setupand/confirm(anonymous). Confirm signs the user in and returnsrecoveryCodesonce.mfaEnabledon the items ofGET /v1/account/users, and error codeMFA_REQUIRED_BY_ACCOUNT.
Changed
- On a policy account,
POST /v1/auth/token/refreshreturns401 MFA_REQUIREDfor a session that did not pass MFA, even without a factor, andDELETE /v1/auth/mfareturns409 MFA_REQUIRED_BY_ACCOUNT.
2026-10-06 — Authenticator step-up
Additive for users without MFA · Changes step-up for users with MFA on
Added
POST /v1/auth/mfa/step-up: a user with MFA on proves step-up with a current authenticator code ({ "action", "code" }) and receives the single-usestepUpToken. Errors:400 INVALID_CODE,409 MFA_NOT_ENABLED,429 LOCKED. Recovery codes are not accepted.POST /v1/email-otp/issueaccepts an optionalaction.- Error codes
MFA_ENROLLMENT_REQUIREDandSTEP_UP_FACTOR_REQUIRED.
Changed
- A user with MFA on gets
403 STEP_UP_FACTOR_REQUIREDfrom the email OTP step-up routes and must usePOST /v1/auth/mfa/step-up. The exception isMfaManage, so a user who lost their authenticator can still disable MFA with a recovery code.
2026-10-06 — Unlink hardening
Additive
Changed
POST /v1/auth/unlinkreturns409 CONFLICTwhen the identity is the user's only sign-in method and the account has no password.- The optional
passwordfield was removed from thePOST /v1/auth/unlinkrequest body. The server never read it, and the step-up is the proof of control. If you still send it, it is ignored.
2026-10-05 — Per-user multi-factor authentication
Additive · Removes operations that never worked
Added
401 MFA_REQUIREDfromPOST /v1/auth/loginandPOST /v1/auth/external-loginwhen the user has MFA enabled, with the extensionsmfaToken,mfaTokenExpiresAtandmfaMethods. No tokens are issued. Users without MFA are unaffected.POST /v1/auth/login/mfa, the anonymous second step (totporrecovery_code).POST /v1/auth/token/refreshcan return401 MFA_REQUIRED(nomfaToken) for a session created before MFA was enabled.- Self-service routes:
GET /v1/auth/mfa,POST /v1/auth/mfa/totp/setup,POST /v1/auth/mfa/totp/confirm,POST /v1/auth/mfa/recovery-codes,DELETE /v1/auth/mfa. Setup, recovery-code regeneration and disable require a step-up token for theMfaManageaction. - Error codes
MFA_REQUIRED,MFA_CHALLENGE_INVALID,MFA_ALREADY_ENABLEDandMFA_NOT_ENABLED. POST /v1/auth/unlinknow requires anUnlinkIdentitystep-up.
Removed
- The earlier
v1/auth/mfaoperationsconfig/{providerId},methods/{providerId},required/{providerId},challengeandchallenge/verify. They never delivered a code and could not complete a verification, so no deprecation period applies. Thev1/auth/mfaprefix is reused by the routes above. - The unpublished delegation (
v1/auth/delegations) and identity-merge (v1/auth/identity-merge) operations, which were never part of the public contract.
2026-08-12 — API baseline
Non-breaking · New public surface
The public API is now represented by one curated OpenAPI contract covering customer authentication, platform operations, messaging, and webhooks. Internal and staff endpoints remain outside the public contract.
Production reference artifacts
Integrators can download the public contract in JSON or YAML format:
/openapi/public.json— OpenAPI JSON document/openapi/public.yaml— OpenAPI YAML document
These endpoints provide the curated contract without exposing the internal Swagger UI or private API documents.
Auth scheme normalization
All public endpoints now carry explicit per-operation security annotations. Endpoints requiring JWT show
jwt only; endpoints accepting either scheme show both alternatives; anonymous endpoints show no
security requirement.
Baseline error responses
All public endpoints now consistently document 401, 403, 422,
429, and 500 responses. All error responses use ProblemDetails format (RFC
7807).
Older changes predating the M41 baseline are not reflected here. This changelog covers the public contract surface only.