Send a care chat message
Run one turn of the cardholder’s conversation with the support agent, or,
with an empty message, open the chat without saying anything. This is
the entry point to the whole surface: a partner who has never called any
other care endpoint starts here.
Render redactedCustomerMessage if present, then reply if present.
That one rule covers every call: an opening, a resume and a turn.
redactedCustomerMessage is what the cardholder typed with card numbers
and other sensitive digit strings masked. Render it, never your own copy of
what they typed, which would put those digits back on their screen and
into your logs.
One turn, synchronously. With a non-empty message the call holds the
connection until the agent has answered, and returns the answer in
reply. An Idempotency-Key header is required, and it is the only way
to recover an answer: nothing on this surface returns a transcript, so a
reply that was produced but not delivered is reachable only by retrying
under the same key, which returns the original reply, with the same id,
and never a sessionToken. A replay requires the same session token its original
request required. Replaying a turn that was sent while the chat could
reach the account needs a valid X-Ls-Care-Session for this chat; without
one it is refused with 401 CARE_SESSION_EXPIRED and returns no reply. A
replay of a turn that ran before the cardholder verified, such as the turn
in which they sent their code, needs no token. A replay never returns a
sessionToken, even if the original response carried one, and it never
cancels a verification or sends a code. So a partner who lost the token
cannot replay an account turn: send the cardholder’s next message as a new
message without a token, which verifies them again (the verification is
cancelled, a new code is sent, and the agent asks for it). A replay is
answered, under the same rule, even after the chat has ended. A retry sent
while the original turn is still running is a replay too: it waits for
that turn’s answer rather than running a second turn. A turn that
failed does not pin its key: the same key may be sent again and will run a
fresh turn.
An empty or absent message opens the chat and runs no turn. On a
cardholder with no open chat it opens one, and reply carries the agent’s
opening line, which is what a chat window renders before anybody has
typed. On a cardholder who already has one it resumes: the chat’s id and
where verification stands come back, and reply does not, because an
opening line arriving mid-conversation reads as the agent introducing
itself a second time. id and redactedCustomerMessage are absent on
both, because nothing was typed. Either way no code is sent and
no Idempotency-Key is needed or consumed, so a widget in a reconnect
loop may still call it as often as it likes. The exception is an opening
addressed to a CareChat:<id> that has ended: it answers
409 CARE_CHAT_ENDED, and the recovery is to address the cardholder as
Customer:<uuid>, which opens a new chat.
chatEnded says when to stop. It is on every 200 body, true or
false, and describes the chat once the call finished: true means the chat
has ended and no further message will be accepted. The agent can end the
chat itself when the cardholder is done; on that turn chatEnded is true
and the closing line is in reply. It is the chat’s state rather than
what this call did, so a replay of that turn also says true. When it is
true, stop sending to that chat.
Verification happens in the conversation, and is never a 401. A
chat that cannot yet reach the account answers 200, and the first
message with text texts the cardholder a code; later messages on the same
chat send no second one. The agent’s own sentence in reply asks for the
code, so a partner that only renders the conversation needs nothing else.
verificationRequired and codeLength are optional hints for an app that
wants a dedicated one-time-code input of the right length, marked as a
one-time code so iOS and Android offer to fill it from the SMS. The app
still sends the code as an ordinary message.
The cardholder completes verification by sending the code as an
ordinary message, and that turn’s response carries the session. When the
code is correct, the chat comes out of that turn able to reach the
account, and the body of that turn carries sessionToken and expiresAt.
No other turn carries them: a session is issued per verification, not per
message. Store the token and send it from the next call onwards. A code
that does not match is answered in reply, not as an error, and the
cardholder can send it again.
A cardholder whose code never arrived asks the agent for another one, in the conversation, and the agent sends a new code to the number on file. There is no separate route for sending a code.
While the chat’s verification is current, send the session token in the
X-Ls-Care-Session header on every message.
A verification goes stale, and the agent then asks for a code again
rather than the call failing. A chat reaches the cardholder’s account
only while its verification is newer than the session lifetime, fifteen
minutes in the default deployment, which is the same window a session
token lives for, so in practice the chat and the token go stale together.
Past that window the next message sent without the header is treated
as an unverified one: a fresh code goes out, the body carries
verificationRequired: true, and the agent’s own reply asks the
cardholder to send the code. The turn still runs and still answers.
A lost or expired session token verifies again rather than being
refused. A new message sent with no X-Ls-Care-Session header, or with a
token past its expiry, on a chat whose verification is still current is
treated the same way: the verification is cancelled before the turn runs,
so the turn cannot reach the account, and the call then proceeds exactly
as on a stale chat. A fresh code goes out, the agent asks the cardholder
for it, and the turn in which they send it returns the next
sessionToken. No special handling is needed: a client that lost its
token simply keeps sending messages.
Only a token that is malformed is refused with CARE_SESSION_EXPIRED, and
a genuine token minted for another chat or another cardholder with
NOT_FOUND, or for another platform with CARE_SESSION_PLATFORM_MISMATCH.
Those are a bug in the caller rather than a lost session, and nothing on
the chat changes. An empty message never reads the header at all, so a
widget still holding a dead token can resume with one and be told where
verification stands without texting anybody.
Everything in the response except chatEnded describes the chat as of
the moment the turn started. The agent can verify a cardholder during a
turn, so a chat that this very call verified still reads
verificationRequired: true here; decide that you hold a session by the
presence of sessionToken.
Requires a token with the VIEW and MANAGE permissions; VIEW alone
is not sufficient, because the agent can freeze, unfreeze and replace the
cardholder’s cards.
Authorizations
API token authentication using format <api token id>:<api client secret>
Headers
A unique identifier for this turn, up to 255 characters. Required whenever message is non-empty; omitting it there is rejected with INVALID_INPUT. A retry carrying the same key returns the first call's answer with the same id (subject to the session rule for replays); aiming the same key at a different message, a different chat or a different cardholder is rejected with 409 CONFLICT. An opening (empty message) neither needs nor consumes a key: one sent anyway is ignored, so that the partner's first real message can still use it.
255"550e8400-e29b-41d4-a716-446655440000"
The sessionToken this route returned on the turn in which the cardholder sent their verification code. Send it on every message while the chat's verification is current. A message without it, or with one past its expiry, is not refused: the chat's verification is cancelled before the turn runs and a new code is sent, so the cardholder verifies again in the conversation. The exception is a replay under an Idempotency-Key whose original request required a session: it must carry a valid token, or it is refused with CARE_SESSION_EXPIRED and returns no reply. A token that is sent is always checked for what it names: a malformed one is refused with CARE_SESSION_EXPIRED, one minted for another chat or another cardholder is NOT_FOUND, and one minted for another platform is CARE_SESSION_PLATFORM_MISMATCH, whether or not it has expired. An opening never reads the header at all, because a widget that reloaded has lost its token and that is the case the opening exists for.
Path Parameters
Who this message is for. Two forms are accepted, and the id's own prefix decides which: a Customer:<uuid> addresses the cardholder and lands on their open chat, opening one if they have none; a CareChat:<uuid> addresses that chat and no other. A bare uuid is read as a chat id. Addressing the cardholder is what lets a partner send their first message in one call instead of two.
Body
One turn of the cardholder's conversation, or, with message empty or absent, the call that opens or resumes the chat without saying anything. Unknown fields are rejected with INVALID_INPUT.
What the cardholder typed, verbatim and unredacted. Send it exactly as they entered it: Grid masks card numbers and other sensitive digit strings and returns the result as redactedCustomerMessage, which is what your UI must render.
Empty, absent, or whitespace-only opens the chat (or resumes an open one) and runs no turn: no agent reply to a message, no SMS, and no Idempotency-Key needed or consumed. A non-empty value runs exactly one turn synchronously and requires an Idempotency-Key header.
"My card was declined at the grocery store"
BCP 47 locale the agent should answer in. Read only by the call that opens the chat and ignored afterwards, because changing it later would rewrite the language earlier turns were answered in. Defaults to en-US.
2 - 32"en-US"
Response
The turn ran, or the chat was opened or resumed. Which fields are present depends on which of those happened.
The result of one call to the message route. Which fields are present depends on what the call did: chatId, chatEnded and verificationRequired are always present; codeLength is added when verification is required and the length is known; a turn adds id, redactedCustomerMessage and reply, and the call that opened the chat adds reply alone, carrying the agent's opening line; and the turn in which the cardholder sent their verification code adds sessionToken and expiresAt. Render redactedCustomerMessage if present, then reply if present.
The chat this call ran on. When the request addressed a cardholder (Customer:<uuid> in the path) this is the chat that was found or opened for them. Use it for every later call on this conversation: later messages and closing the chat.
"CareChat:019542f5-b3e7-1d02-0000-000000000030"
Whether the chat has ended as of this response, so no further message will be accepted. Present on every response, true or false, so a client reads a field rather than an absence. It is the chat's state, not whether this call ended it: on the turn where the agent ends the chat it is true and the closing line is in reply, and a replay of that turn after the chat ended is also true. When it is true, stop sending to this chat; to talk to the cardholder again, address them as Customer:<uuid>, which opens a new chat.
false
An optional hint: whether the cardholder still has to verify before the agent can reach their account. Present on every response, true or false. A partner can ignore it, because when a code is needed the agent asks for it in reply; it lets an app show a dedicated code input beside the chat. Either way, the cardholder sends the code as an ordinary message. An outstanding verification is never reported as a 401.
It describes the chat as of the moment the turn started, so the one turn in which the cardholder sent their code reports verificationRequired: true and carries sessionToken as well. Decide that you hold a session by the presence of sessionToken, never by this field going false.
true
Id of the cardholder's message. Present only when a turn ran, so absent on an opening or a resume. A retry under the same Idempotency-Key returns the same id, so a client that has already rendered this id can skip the exchange instead of showing it twice.
"CareMessage:019542f5-b3e7-1d02-0000-000000000032"
What the cardholder typed, with card numbers and other sensitive digit strings masked, and the string your app must render in the transcript. Render this, never your own copy of what the cardholder typed: your copy would put a card number they pasted back on their screen and into your logs. Present only when a turn ran.
"My card [redacted card number ending 1111] was declined"
What the agent said on this call, to render as the assistant's message. Present whenever the agent said something: its answer when a turn ran, or its opening line on the call that opened the chat. Absent on a resume. An opening needs no Idempotency-Key and consumes none, even though it carries a reply. A retry under the same Idempotency-Key returns the same reply and the same id, provided it carries the session token its original request required: retrying a turn sent while the chat could reach the account needs a valid X-Ls-Care-Session, and without one is refused with 401 CARE_SESSION_EXPIRED; a turn that ran before the cardholder verified can be retried without one. A retry never carries a sessionToken, even if the original response did. When verification is outstanding the agent's own sentence asks for the code, and when a code the cardholder sent did not match, this is where they are told so.
"I can help with that. To look at your account I've texted you a code. What does it say?"
An optional hint: the number of digits in the code, so an app can show a one-time-code input of the right length, marked as a one-time code so iOS and Android offer to fill it from the SMS. The app still sends the code as an ordinary message. Absent when the length is not known; leave the input unbounded rather than guessing, since a wrong length makes every correct code look like a typo.
6
The session token for this chat, present only on the turn in which the cardholder verified themselves, and absent on every other turn.
The cardholder completes verification by sending the SMS code as an ordinary message. When the code is correct, the chat comes out of that turn able to reach the account, and this field is how the partner receives the credential that turn earned. It is the only place a session token is ever issued. A later message sent without it cancels the verification and asks the cardholder for a new code.
Send it as the X-Ls-Care-Session header from the next call onwards. It is returned once and never re-readable, it cannot be revoked, and it is bound to this platform, this chat and this cardholder. Never log it, never put it in a URL, and never store it past expiresAt.
One session is issued per verification rather than per message, so an ordinary turn on a chat that is already verified carries no sessionToken. The one you are already holding is still the right one.
A replay under the same Idempotency-Key never returns a sessionToken, even if the original response carried one, and a replay of a turn that required a session needs that session itself. So if the response carrying the token was lost, you cannot recover it or replay an account turn: send the cardholder's next message as a new message without a token. The verification is cancelled, a new code is sent, and the agent asks for it. A replay of the turn in which the cardholder sent their code needs no token and returns the original reply without one.
"eyJwbGF0Zm9ybV9pZCI6IjAxOTU0MmY1LWIzZTctMWQwMi0wMDAwLTAwMDAwMDAwMDAwMSJ9.aGVsbG8.c2lnbmF0dXJl"
ISO 8601 timestamp, in UTC, after which the sessionToken beside it no longer counts as a session: a message carrying it past this time is treated as carrying none, and the cardholder is asked to verify again. Present exactly when sessionToken is present. Read it rather than assuming a duration.
"2026-09-12T14:25:00Z"