[grid] add SMS OTP auth API#617
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
✱ Stainless preview builds for gridThis PR will update the cli csharp go kotlin openapi php python ruby typescript ✅ grid-ruby studio · code
|
f5862f7 to
ed1398a
Compare
|
ed1398a to
0119e58
Compare
|
this comment is expected and shouldnt matter because nobody is using this stuff yet |
Greptile SummaryThis PR introduces
Confidence Score: 5/5This is a documentation-only change that adds SMS OTP as a new auth method. All schema compositions, discriminator mappings, error codes, and flow descriptions are accurate and consistent with existing patterns. The new SMS_OTP schemas follow the identical allOf composition and discriminator pattern established by EMAIL_OTP with no deviations. Error codes, session field omission, and two-leg flow documentation are all correct. The only gaps are missing example entries in a couple of response/request blocks, which do not affect schema correctness or API behavior. auth_credentials_{id}_verify.yaml is missing a 202 smsOtp response example and a smsOtpSignedRetry request body example — purely documentation gaps that do not affect the schema.
|
| Filename | Overview |
|---|---|
| openapi/components/schemas/auth/SmsOtpCredentialCreateRequest.yaml | New schema composing AuthCredentialCreateRequest + SmsOtpCredentialCreateRequestFields via allOf; mirrors the EmailOtp pattern exactly. |
| openapi/components/schemas/auth/SmsOtpCredentialVerifyRequestFields.yaml | Requires type and encryptedOtpBundle; includes accurate description of the HPKE two-leg OTP flow and a well-formed example ciphertext blob. |
| openapi/components/schemas/auth/AuthCredentialCreateRequestOneOf.yaml | Added SMS_OTP branch to the oneOf list and discriminator mapping; correctly references SmsOtpCredentialCreateRequest. |
| openapi/components/schemas/auth/AuthCredentialResponseOneOf.yaml | Added SMS_OTP → AuthMethodResponse mapping in the challenge-response discriminator; correctly reuses AuthMethodResponse just as EMAIL_OTP does. |
| openapi/components/schemas/auth/AuthMethodType.yaml | SMS_OTP added to the enum and its description; all downstream schemas that reference this type inherit the new value correctly. |
| openapi/components/schemas/errors/Error400.yaml | SMS_OTP_CREDENTIAL_ALREADY_EXISTS added to both the description table and the enum; description text is accurate and consistent with EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS. |
| openapi/paths/auth/auth_credentials.yaml | POST and GET credential endpoints fully updated for SMS OTP: new request/response examples, updated 201/202/400 descriptions; GET list example omits SMS_OTP credential (P2). |
| openapi/paths/auth/auth_credentials_{id}_challenge.yaml | Challenge endpoint descriptions updated to include SMS OTP; 200 response examples still missing an smsOtp entry (flagged in a previous review comment). |
| openapi/paths/auth/auth_credentials_{id}_verify.yaml | Verify endpoint descriptions and request body examples updated for SMS OTP; 202 response missing smsOtp example and request body missing smsOtpSignedRetry example (P2). |
| openapi/components/schemas/auth/AuthMethodResponse.yaml | Updated description now correctly covers both EMAIL_OTP and SMS_OTP; otpEncryptionTargetBundle documentation is accurate for both OTP types. |
| openapi/components/schemas/auth/AuthSession.yaml | Updated encryptedSessionSigningKey description to correctly state that SMS_OTP sessions omit this field for the same reason as EMAIL_OTP (client holds TEK private key). |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant Grid as Grid API
Note over Client,Grid: POST /auth/credentials (Add SMS OTP)
Client->>Grid: "POST /auth/credentials {type: SMS_OTP, accountId}"
Grid-->>Client: "202 AuthSignedRequestChallenge {payloadToSign, requestId}"
Client->>Grid: POST /auth/credentials (signed retry + Grid-Wallet-Signature + Request-Id)
Grid-->>Client: "201 AuthMethodResponse {type: SMS_OTP, nickname, otpEncryptionTargetBundle}"
Note over Client,Grid: POST /auth/credentials/{id}/challenge (Re-issue OTP)
Client->>Grid: "POST /auth/credentials/{id}/challenge (empty body)"
Grid-->>Client: "200 AuthMethodResponse {type: SMS_OTP, otpEncryptionTargetBundle}"
Note over Client,Grid: POST /auth/credentials/{id}/verify (Login)
Client->>Client: "HPKE-encrypt {otp_code, TEK_public_key} to encryptedOtpBundle"
Client->>Grid: "POST /auth/credentials/{id}/verify {type: SMS_OTP, encryptedOtpBundle}"
Grid-->>Client: "202 AuthSignedRequestChallenge {type: SMS_OTP, payloadToSign, requestId}"
Client->>Client: Sign verificationToken with TEK private key
Client->>Grid: "POST /auth/credentials/{id}/verify (signed retry)"
Grid-->>Client: "200 AuthSession {type: SMS_OTP, nickname, expiresAt}"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant Grid as Grid API
Note over Client,Grid: POST /auth/credentials (Add SMS OTP)
Client->>Grid: "POST /auth/credentials {type: SMS_OTP, accountId}"
Grid-->>Client: "202 AuthSignedRequestChallenge {payloadToSign, requestId}"
Client->>Grid: POST /auth/credentials (signed retry + Grid-Wallet-Signature + Request-Id)
Grid-->>Client: "201 AuthMethodResponse {type: SMS_OTP, nickname, otpEncryptionTargetBundle}"
Note over Client,Grid: POST /auth/credentials/{id}/challenge (Re-issue OTP)
Client->>Grid: "POST /auth/credentials/{id}/challenge (empty body)"
Grid-->>Client: "200 AuthMethodResponse {type: SMS_OTP, otpEncryptionTargetBundle}"
Note over Client,Grid: POST /auth/credentials/{id}/verify (Login)
Client->>Client: "HPKE-encrypt {otp_code, TEK_public_key} to encryptedOtpBundle"
Client->>Grid: "POST /auth/credentials/{id}/verify {type: SMS_OTP, encryptedOtpBundle}"
Grid-->>Client: "202 AuthSignedRequestChallenge {type: SMS_OTP, payloadToSign, requestId}"
Client->>Client: Sign verificationToken with TEK private key
Client->>Grid: "POST /auth/credentials/{id}/verify (signed retry)"
Grid-->>Client: "200 AuthSession {type: SMS_OTP, nickname, expiresAt}"
Reviews (3): Last reviewed commit: "docs(grid): address SMS OTP auth OpenAPI..." | Re-trigger Greptile

Overview
SMS_OTPas a first-class auth method discriminator.400 Error400with codeSMS_OTP_CREDENTIAL_ALREADY_EXISTS.openapi/.Endpoint Shape After This Change
No endpoint path or header changes. This adds
SMS_OTPas another discriminator branch in the existing Embedded Wallet Auth credential flows.POST /auth/credentialsnow accepts an SMS OTP create request:{ "type": "SMS_OTP", "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" }The unsigned add-credential request still returns
202 AuthSignedRequestChallenge:{ "type": "SMS_OTP", "payloadToSign": "...", "requestId": "Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21", "expiresAt": "2026-04-08T15:35:00Z" }The signed retry returns
201 AuthMethodResponse:{ "id": "AuthMethod:019542f5-b3e7-1d02-0000-000000000001", "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002", "type": "SMS_OTP", "nickname": "+14155550123", "otpEncryptionTargetBundle": "{...}", "createdAt": "2026-04-08T15:30:01Z", "updatedAt": "2026-04-08T15:30:01Z" }If the target wallet already has an SMS OTP credential,
POST /auth/credentialsreturns400 Error400:{ "status": 400, "code": "SMS_OTP_CREDENTIAL_ALREADY_EXISTS", "message": "This wallet already has an SMS_OTP credential. Delete the existing one via DELETE /auth/credentials/{id} before adding a new phone number." }POST /auth/credentials/{id}/challengetreats SMS OTP like email OTP: the request body can be empty, and the response is200 AuthMethodResponsewithtype: "SMS_OTP"plus a freshotpEncryptionTargetBundle.{}POST /auth/credentials/{id}/verifynow accepts an SMS OTP verify request:{ "type": "SMS_OTP", "encryptedOtpBundle": "{\"encappedPublic\":\"...\",\"ciphertext\":\"...\"}" }The first verify leg returns
202 AuthSignedRequestChallenge; the signed retry sends the same body withGrid-Wallet-SignatureandRequest-Id, then returns200 AuthSession. SMS OTP sessions omitencryptedSessionSigningKeybecause the client keeps the TEK private key.{ "id": "Session:019542f5-b3e7-1d02-0000-000000000003", "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000002", "type": "SMS_OTP", "nickname": "+14155550123", "expiresAt": "2026-04-08T16:30:01Z" }Downstream
grid-apiclient/spec output.Test Plan
make buildmake lint(passes; existing warning output remains)