> ## Documentation Index
> Fetch the complete documentation index at: https://agents-docs.lightspark.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Login & sessions

> The end-user SCA login and the session it grants

> [!NOTE]
>
> Applies only to customers in an SCA-required region (EU). Every endpoint here
> returns **`409`** for other customers.

Per-transaction authorization gates individual debits. The **SCA login** is
separate: it authenticates the end user to open a longer-lived session that
covers reads and account access beyond the per-transaction window. Grid provides
the login plumbing; your application decides when to drive it (for example, when
a customer opens their account and the previous session has lapsed).

> [!NOTE]
>
> **A customer's EUR / USDC accounts aren't provisioned until their first SCA
> login after KYC approval.** Provisioning is deferred from KYC-approval time to
> the first login that opens a valid SCA session, so a freshly KYC-approved
> customer's EUR / USDC accounts won't appear in `GET /customers/internal-accounts`
> until then. Expect those accounts to be unavailable, and drive the SCA login
> once KYC is approved, before relying on them.

All paths below are relative to `https://api.lightspark.com/grid/2025-10-13`.

## Logging in

**1. Start the login**

```bash theme={null}
POST /sca/login/start?customerId={customerId}

{ "factor": "SMS_OTP" }
```

The response carries only what the chosen factor needs:

* **`SMS_OTP`**: a code is dispatched; you get back `challengeId` and `expiresAt`.
* **`TOTP`**: nothing extra; the customer reads the code from their app.
* **`PASSKEY`**: WebAuthn `passkeyOptions` (with `allowedOrigins` and `relyingPartyId`) to pass to the device.

The factor must already be enrolled (or, for `SMS_OTP`, the phone verified). See
[factor enrollment](https://agents-docs.lightspark.com/platform-overview/sca/factor-enrollment.md).

**2. Complete the login**

Submit the proof for the factor you started with: `code` for `SMS_OTP` / `TOTP`
(echoing `challengeId` for `SMS_OTP`), or `passkeyAssertion` + `origin` for
`PASSKEY`. Every completion also requires **`endUserIpAddress`** — the IP of the
end user's device the login is performed from, recorded against the login event
and fed into risk assessment. Supply the customer's address, not your server's.

```bash theme={null}
POST /sca/login/complete?customerId={customerId}

{ "factor": "SMS_OTP", "challengeId": "…", "code": "000000", "endUserIpAddress": "203.0.113.42" }
```

```json theme={null}
{ "status": "SUCCESS", "sessionExpiresAt": "2026-01-29T12:00:00Z" }
```

A `status` of `SUCCESS` opens the session and revokes any previous SCA session
for that customer, and `sessionExpiresAt` gives its absolute expiry — prompt a
re-login ahead of it rather than waiting for a call to fail. Any other `status`
value means the login did not complete; treat only `SUCCESS` as success. An
invalid or expired proof — or a missing or malformed `endUserIpAddress` —
returns `400`. **In sandbox, the code is always `000000`.**

## Session scope and fresh authentication

An active SCA login session covers EUR / USDC account reads for 180 days; the
login-complete response's `sessionExpiresAt` gives the exact timestamp it
lapses. A request for transaction history older than 90 days requires fresh SCA,
even when the broader session has not expired. Money movement in SCA-regulated
currencies is refused with **`409` `SCA_SESSION_REQUIRED`** once the session
passes, so track `sessionExpiresAt` and drive a re-login before it does. When
Grid indicates that a session is missing, expired, or too old for the requested
history, restart the login flow before retrying the read.

## Account-security signals

Grid runs an adaptive-authentication risk engine that maintains each customer's
login-security state. Because your application owns the customer's login, you
report the security-relevant events it sees so the engine can act on them:

```bash theme={null}
POST /sca/record-event?customerId={customerId}

{ "eventType": "FAILED_LOGIN_ATTEMPT", "endUserIpAddress": "203.0.113.42" }
```

Returns the customer's resulting login-security state so you can surface a
lockout — `{ eventType, suspended, lockedUntil, failedAttempts }`. When the
customer is locked out, this endpoint, `POST /sca/login/complete`, and
[quote authorization](https://agents-docs.lightspark.com/platform-overview/sca/per-transaction-authorization.md)
return `423` with `details.lockedUntil` (when they may retry) and
`details.failedAttempts`.
`eventType` must be one of:

| `eventType` | Effect |
| - | - |
| `FAILED_LOGIN_ATTEMPT` | Increments the failed-login counter and escalates a lockout: **5 → 15 min, 6 → 30 min, 7 → 1 hour, 8 → 24 hours, 9 or more → suspension.** |
| `RESET_PASSWORD_COMPLETED` | Revokes every active SCA session for the customer and clears the failed-login counter. |

Report `FAILED_LOGIN_ATTEMPT` on each failed sign-in and `RESET_PASSWORD_COMPLETED`
once a password recovery finishes. Any other value returns `400`.

> [!NOTE]
>
> The failed-login counter is cumulative and is **not** reset by a successful
> login; only `RESET_PASSWORD_COMPLETED` clears it. Record that event after a
> password recovery to zero the counter and clear a time-bounded lockout, rather
> than relying on the customer simply logging in again. A suspended customer
> (9 or more failed attempts, locked with no automatic expiry) clears the
> suspension the same way — a completed password reset.

## Your responsibilities

Grid provides the SCA endpoints and risk decisions; your application owns the
end-user login and session experience. Report every failed sign-in and completed
password recovery through `record-event`, enforce any returned lockout before
offering another login attempt, and do not store or reuse a customer's TOTP
secret or passkey material. Treat TOTP secrets and WebAuthn ceremony data as
end-user credentials, not platform credentials.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.