Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 71 additions & 27 deletions mintlify/openapi.yaml

Large diffs are not rendered by default.

98 changes: 71 additions & 27 deletions openapi.yaml

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions openapi/components/schemas/auth/AuthMethodResponse.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@ description: >-
`AuthMethod` is `unevaluatedProperties: false`, which disambiguates the
oneOf against `PasskeyAuthChallenge` — without the strictness, an
`AuthMethod` with extra fields would ambiguously match both branches.


For `EMAIL_OTP` credentials, the response also carries
`otpEncryptionTargetBundle` so the client can HPKE-encrypt the OTP code
in the subsequent `POST /auth/credentials/{id}/verify` call without the
plaintext code ever transiting the server.
allOf:
- $ref: ./AuthMethod.yaml
- type: object
properties:
otpEncryptionTargetBundle:
type: string
description: >-
HPKE encryption target bundle for the freshly initiated OTP
challenge. Returned only for `EMAIL_OTP` credentials. The client
generates an ephemeral P-256 keypair (the Target Encryption
Key, or TEK) and uses this bundle to HPKE-encrypt
`{clientPublicKey, otpCodeAttempt}` together; the encrypted
payload is submitted as `encryptedOtpBundle` on
`POST /auth/credentials/{id}/verify`. The bundle is one-time-use
per OTP issuance — re-issue via
`POST /auth/credentials/{id}/challenge` to obtain a fresh
bundle. The matching TEK private key must remain on the client
and is used to sign the `verificationToken` returned on the
subsequent signed-retry.
example: pl5GAuS7r34v8r1Y2J3aoLZ9N9j-jJpvxz-fkU6FQ8FmKbT-MQ7sTwL1mEZ3FfVo
unevaluatedProperties: false
13 changes: 7 additions & 6 deletions openapi/components/schemas/auth/AuthSession.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ allOf:
Wallet requests until `expiresAt`.


Only returned from session-issuing responses like
`POST /auth/credentials/{id}/verify` and
`POST /auth/sessions/{id}/refresh`. Omitted from responses that
simply surface existing sessions (e.g. `GET /auth/sessions`) —
Grid does not retain the plaintext key after the client has
decrypted it.
Returned only by session-issuing responses for `OAUTH` and
`PASSKEY` credentials. `EMAIL_OTP` sessions omit this field —
the client generates a TEK keypair before verification and
retains the private key throughout, so the server has nothing
to deliver. Always omitted from list responses
(`GET /auth/sessions`) since Grid does not retain the
plaintext key after the client has decrypted it.
example: w99a5xV6A75TfoAUkZn869fVyDYvgVsKrawMALZXmrauZd8hEv66EkPU1Z42CUaHESQjcA5bqd8dynTGBMLWB9ewtXWPEVbZvocB4Tw2K1vQVp7uwjf
expiresAt:
type: string
Expand Down
36 changes: 26 additions & 10 deletions openapi/components/schemas/auth/AuthSignedRequestChallenge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@ title: Authentication Signed Request Challenge
description: >-
202 response returned from Embedded Wallet Auth endpoints that require
a signed retry — `POST /auth/credentials` (adding an additional
credential), `DELETE /auth/credentials/{id}` (revoking a credential), and
`DELETE /auth/sessions/{id}` (revoking a session). Carries the signing
fields from `SignedRequestChallenge` plus the `type` of the authentication
credential involved (being added, revoked, or that issued the session being
revoked). The client already knows the target resource id from the request
path / body it just sent, so nothing beyond `type` is echoed in the response.
credential), `DELETE /auth/credentials/{id}` (revoking a credential),
`DELETE /auth/sessions/{id}` (revoking a session), and the `EMAIL_OTP`
branch of `POST /auth/credentials/{id}/verify` (the secure OTP login
flow, where the client submits an `encryptedOtpBundle` and receives a
`verificationToken` to sign for the second-leg session issuance).
Carries the signing fields from `SignedRequestChallenge` plus the
`type` of the authentication credential involved (being added,
revoked, that issued the session being revoked, or being
authenticated). The client already knows the target resource id from
the request path / body it just sent, so nothing beyond `type` is
echoed in the response.


The keypair used to compute the stamp depends on the operation. For
credential / session management retries, sign with the session API
keypair of an existing verified credential on the same internal
account. For the `EMAIL_OTP` verify retry, sign with the ephemeral
Target Encryption Key (TEK) the client generated for this login —
its public key is the one carried inside the `encryptedOtpBundle`
and bound into the `verificationToken`, and it becomes the client's
session API key on successful completion.
allOf:
- $ref: ../common/SignedRequestChallenge.yaml
- type: object
Expand All @@ -18,7 +33,8 @@ allOf:
$ref: ./AuthMethodType.yaml
description: >-
Credential type relevant to this challenge: the credential type
being added (`POST /auth/credentials`) or revoked
(`DELETE /auth/credentials/{id}`). For session revocation, this is
the type of credential that issued the session
(`DELETE /auth/sessions/{id}`).
being added (`POST /auth/credentials`), revoked
(`DELETE /auth/credentials/{id}`), or authenticated
(`EMAIL_OTP` branch of `POST /auth/credentials/{id}/verify`).
For session revocation, this is the type of credential that
issued the session (`DELETE /auth/sessions/{id}`).
Original file line number Diff line number Diff line change
@@ -1,25 +1,50 @@
type: object
required:
- type
- otp
- clientPublicKey
- encryptedOtpBundle
description: >-
Verify an email-OTP credential via the secure two-leg flow. The client
HPKE-encrypts `{clientPublicKey, otpCodeAttempt}` under the
`otpEncryptionTargetBundle` returned from the credential's
registration or `POST /auth/credentials/{id}/challenge`, submits the
bundle here, and receives `202` with a `payloadToSign` carrying a
`verificationToken` bound to the client's ephemeral public key. The
client signs that token with the matching private key and retries
this request with `Grid-Wallet-Signature` + `Request-Id` headers to
obtain the session. Plaintext OTP codes are never sent over the wire.
properties:
type:
type: string
enum:
- EMAIL_OTP
description: Discriminator value identifying this as an email OTP verification.
otp:
type: string
description: The one-time password received by the user via email.
example: '123456'
clientPublicKey:
encryptedOtpBundle:
type: string
description: >-
Client-generated P-256 public key, hex-encoded in uncompressed SEC1
format (0x04 prefix followed by the 32-byte X and 32-byte Y
coordinates; 130 hex characters total). The matching private key
must remain on the client. Grid encrypts the session signing key
returned in the response to this public key. The key is ephemeral
and one-time-use per verification request.
example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2
HPKE-encrypted payload that carries the OTP code without it ever
transiting Grid in plaintext. The client generates a fresh
ephemeral P-256 key pair — the session signing key pair it will use
once the login completes — and HPKE-seals the JSON object
`{clientPublicKey, otpCodeAttempt}`, where `clientPublicKey` is that
key pair's public key, to the encryption target in the
`otpEncryptionTargetBundle` returned from the credential's
`POST /auth/credentials` registration response or
`POST /auth/credentials/{id}/challenge` re-issue response, then
submits the sealed result here. Grid is a pass-through and never
sees the plaintext OTP code.


Encrypt with a standard HPKE library (RFC 9180) using the suite
DHKEM(P-256, HKDF-SHA256) / HKDF-SHA256 / AES-256-GCM, and submit
the library's output as a base64url string. The Global Accounts
client-keys guide shows the key-pair generation and HPKE calls.


On success the response is `202` with a `payloadToSign` carrying a
`verificationToken` bound to the public key sealed in this bundle.
Sign that token with the corresponding private key, then retry this
request with the full stamp in `Grid-Wallet-Signature` and the
`requestId` in `Request-Id` to complete the flow and receive the
session. The client retains that private key as the session signing
key, and its public key becomes the session API key.
example: AmU3w-q1z5RbX1J4eA9JmCnQ4kEZxOIspz1L3HxxPxYkBPlSC5Tdl2dGTzYwK1rh0Hcp4yEoBMpEcWnxoPpzS7M
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ properties:
JWT-shaped with supported `iss`, non-empty `aud` and `sub`, numeric `iat`
and `exp`, and `iat` less than 60 seconds before the request timestamp,
but the signature segment may be a dummy value.
example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature
example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.-3_ETmSGOl4wGNLR1QSOMlHk5IvADpX3YdHFmTH9KmRu6sEhM20RsURjKrI4-_EKj7J_HtsdS1tCHm0iw2J0qtoczYFQqEW_U9qJD6QsuvTFx8Fj9rFa3ieYhZKi3kkBu6cADogUiudP50kf9345ATys2GrYm-ba5esgReW1WzGJG3SgCyIDnHFfxmeLjE2YE9EFxT73To3mPYAk0ywPL2MpFFV9F8I3PsnbDAxinaY75GeA8vJXATr8weEIXqHD2lxmXVE95qd2ZlcuyLUaEYyp9GXcOnx7SjhdJG88jl5BZQvxOVgBMo42iGjK674lSwsMiHpzLX98j6C786Rd9Q
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ properties:
be JWT-shaped with supported `iss`, non-empty `aud` and `sub`, numeric
`iat` and `exp`, and a `nonce` equal to `sha256(clientPublicKey)`, but
the signature segment may be a dummy value.
example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature
example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.-3_ETmSGOl4wGNLR1QSOMlHk5IvADpX3YdHFmTH9KmRu6sEhM20RsURjKrI4-_EKj7J_HtsdS1tCHm0iw2J0qtoczYFQqEW_U9qJD6QsuvTFx8Fj9rFa3ieYhZKi3kkBu6cADogUiudP50kf9345ATys2GrYm-ba5esgReW1WzGJG3SgCyIDnHFfxmeLjE2YE9EFxT73To3mPYAk0ywPL2MpFFV9F8I3PsnbDAxinaY75GeA8vJXATr8weEIXqHD2lxmXVE95qd2ZlcuyLUaEYyp9GXcOnx7SjhdJG88jl5BZQvxOVgBMo42iGjK674lSwsMiHpzLX98j6C786Rd9Q
clientPublicKey:
type: string
description: >-
Expand Down
10 changes: 7 additions & 3 deletions openapi/paths/auth/auth_credentials.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ post:
value:
type: OAUTH
accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature
oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.-3_ETmSGOl4wGNLR1QSOMlHk5IvADpX3YdHFmTH9KmRu6sEhM20RsURjKrI4-_EKj7J_HtsdS1tCHm0iw2J0qtoczYFQqEW_U9qJD6QsuvTFx8Fj9rFa3ieYhZKi3kkBu6cADogUiudP50kf9345ATys2GrYm-ba5esgReW1WzGJG3SgCyIDnHFfxmeLjE2YE9EFxT73To3mPYAk0ywPL2MpFFV9F8I3PsnbDAxinaY75GeA8vJXATr8weEIXqHD2lxmXVE95qd2ZlcuyLUaEYyp9GXcOnx7SjhdJG88jl5BZQvxOVgBMo42iGjK674lSwsMiHpzLX98j6C786Rd9Q
passkey:
summary: Add a passkey credential
value:
Expand All @@ -89,8 +89,11 @@ post:
description: >-
Authentication credential created successfully. The body is the
created `AuthMethod` for all three credential types. For `EMAIL_OTP`,
the email is the customer email tied to the internal account. For
`PASSKEY`, the credential must be authenticated for the first time via
the email is the customer email tied to the internal account, and
the response also carries `otpEncryptionTargetBundle` — the HPKE
target bundle the client uses to encrypt the OTP attempt on the
subsequent `POST /auth/credentials/{id}/verify`. For `PASSKEY`,
the credential must be authenticated for the first time via
`POST /auth/credentials/{id}/challenge` followed by
`POST /auth/credentials/{id}/verify` to produce a session — there
is no inline authentication challenge on the registration response.
Expand All @@ -106,6 +109,7 @@ post:
accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
type: EMAIL_OTP
nickname: example@lightspark.com
otpEncryptionTargetBundle: pl5GAuS7r34v8r1Y2J3aoLZ9N9j-jJpvxz-fkU6FQ8FmKbT-MQ7sTwL1mEZ3FfVo
createdAt: '2026-04-08T15:30:01Z'
updatedAt: '2026-04-08T15:30:01Z'
oauth:
Expand Down
12 changes: 7 additions & 5 deletions openapi/paths/auth/auth_credentials_{id}_challenge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ post:


For `EMAIL_OTP` credentials, this triggers a new one-time password
email to the address on file. The response is a plain `AuthMethod`;
there is no challenge body to surface because the OTP is delivered
out-of-band via email. After the user receives the new OTP, call
`POST /auth/credentials/{id}/verify` to complete verification and
issue a session.
email to the address on file and returns a fresh
`otpEncryptionTargetBundle` for the client to HPKE-encrypt the OTP
attempt against. After the user receives the new OTP, build the
`encryptedOtpBundle` under the new target bundle and call
`POST /auth/credentials/{id}/verify` to begin the secure OTP login
flow.


`OAUTH` credentials do not have a challenge step. To authenticate or
Expand Down Expand Up @@ -84,6 +85,7 @@ post:
accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002
type: EMAIL_OTP
nickname: example@lightspark.com
otpEncryptionTargetBundle: pl5GAuS7r34v8r1Y2J3aoLZ9N9j-jJpvxz-fkU6FQ8FmKbT-MQ7sTwL1mEZ3FfVo
createdAt: '2026-04-08T15:30:01Z'
updatedAt: '2026-04-08T15:35:00Z'
passkey:
Expand Down
Loading
Loading