Lightspark Embedded Care API
A support chat between the cardholder and Lightspark's care agent, driven from the partner's own app. The platform calls with its ordinary Grid credential, but the person typing is the cardholder rather than the caller, so reaching that cardholder's account takes a second proof: an SMS code they send into the chat, after which the partner carries a session token in the `X-Ls-Care-Session` header. **The surface is two routes, and verification happens inside the conversation.** 1. **Open the chat.** `POST /care/chats/{chatId}/messages` with an empty `message`, addressed to the cardholder as `Customer:`. Returns the chat's id and the agent's opening line in `reply`, which is what a chat window renders before anybody has typed. Sends no SMS, needs no `Idempotency-Key` and consumes none, and is safe to call again: a widget that reloaded gets the same chat back, this time without a `reply`. 2. **Send the cardholder's first message.** The same route with their text and an `Idempotency-Key` header. One turn runs synchronously and the agent's answer comes back in `reply`. On a chat that cannot yet reach the account this call also texts the cardholder a code, and the agent's `reply` asks for it. 3. **The cardholder sends the code as an ordinary message.** When it is correct, the response to that turn carries `sessionToken` and `expiresAt`. Send that token as the `X-Ls-Care-Session` header on every message from then on. A code that does not match is answered in `reply`, and the cardholder can simply send it again. 4. **Close the chat** with `DELETE /care/chats/{chatId}` when the cardholder is done. Every response also carries `chatEnded`, and the agent can end the chat itself: when `chatEnded` is true, the closing line is in `reply` and the chat takes no further messages, so stop sending to it. An opening addressed to that `CareChat:` answers `409 CARE_CHAT_ENDED`; address the cardholder as `Customer:` to open a new chat. **Render `redactedCustomerMessage` if present, then `reply` if present.** That one rule covers an opening, a resume and a turn. **A cardholder whose code never arrived asks the agent for another one.** The agent sends a new code to the number on file and says so in its `reply`. There is no separate route for it. **The verification fields are optional hints.** `verificationRequired` and `codeLength` let an app show 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. A partner can ignore them, since the agent asks for the code in its reply and says where it went, and the app still sends the code as an ordinary message. Step 1 is optional. A message addressed to a `Customer:` id opens that cardholder's chat if they have none, so a partner whose cardholder has just typed something spends one call rather than two. A cardholder who has not verified yet is never a `401`, because the first message from every new cardholder is unverified and a `401` there would be indistinguishable from a bad credential. On these endpoints a `401` means the platform's Grid credential is wrong, the session token presented is malformed, or a replay lacks the session token its original request required. **Verification expires, and a lost session is recoverable in the chat.** 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 also how long a session token lives. Past that window a message gets a fresh code, and the agent asks the cardholder to send it back inside the conversation, which hands the partner the next session token. 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, a new code is sent, and the agent asks for it. So a client that lost its token, or held one too long, simply keeps sending messages. Only a token that is malformed, or that was minted for another platform, chat or cardholder, is refused. Two things an integration has to get right: - **Render `redactedCustomerMessage`, never your own copy of what was typed.** Grid masks card numbers and other sensitive digit strings in it. A UI that renders its own copy of the input instead puts a card number the cardholder pasted back on their screen unmasked, and into your logs. - **The session token is returned once and is never re-readable.** No endpoint hands it back, it cannot be revoked, and it is short-lived by design. Hold it for the life of the cardholder's session. It arrives on the message response to the turn in which the cardholder sent their code, and on no other. Losing it costs the cardholder a new code, never access to the chat. Some things are absent by construction, so no integration can come to depend on them: nothing returns a transcript or an earlier turn's reply (retrying under the original `Idempotency-Key` is how an answer that was produced but not delivered is recovered; a replay requires the same session token its original request required and never carries a `sessionToken`, so a lost token is recovered by sending the cardholder's next message as a new message, which verifies them again), nothing sets a verification state, and nothing changes the number a code is sent to. Errors from these endpoints carry `code` and `reason` only (see the `CareError` schema), unlike the `Error4xx` schemas used elsewhere in this specification.