[grid] add customer phoneNumber API surface#626
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
|
jklein24
left a comment
There was a problem hiding this comment.
@DhruvPareek I actually need this same change for Striga. Is this ready for review?
|
this PR is ready, it's tied to this stack of pr's (all of the draft pr's are related to the phone number stuff), but that stack isn't ready yet |
|
i can clean it all up tomorrow morning and send it over for review though if that works? |
|
or if you're stuff isn't related to my webdev stack dw about it |
Greptile SummaryThis PR adds
Confidence Score: 4/5Safe to merge with the schema gap noted; the server will still reject invalid requests, but client tooling won't warn before they're sent. The
|
| Filename | Overview |
|---|---|
| openapi/components/schemas/customers/CustomerUpdateRequest.yaml | Adds phoneNumber field with E.164 regex, but missing a not constraint to enforce the documented email+phoneNumber mutual-exclusivity rule. |
| openapi/components/schemas/customers/Customer.yaml | Adds phoneNumber to response schema with correct E.164 pattern and consistent description wording. |
| openapi/components/schemas/customers/CustomerCreateRequest.yaml | Adds phoneNumber to create request schema; no mutual-exclusivity restriction needed here (create legitimately accepts both email and phone). |
| openapi/paths/customers/customers_{customerId}.yaml | Comprehensively updates PATCH description, header docs, examples, and all response codes for phone; 400 description omits the combined-contact rejection case. |
| openapi/paths/customers/customers.yaml | Adds phoneNumber to all three POST request body examples; changes are additive and correct. |
| openapi.yaml | Bundled file mirroring all component and path changes; inherits the same CustomerUpdateRequest schema gap. |
| mintlify/openapi.yaml | Mintlify copy of the bundled spec; identical changes and the same inherited schema gap. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant Grid API
Note over Client,Grid API: Phone update – no SMS_OTP credentials tied
Client->>Grid API: PATCH /customers/{id} {phoneNumber}
Grid API-->>Client: 200 Updated customer (phoneNumber set)
Note over Client,Grid API: Phone update – SMS_OTP credentials tied (signed-retry)
Client->>Grid API: PATCH /customers/{id} {phoneNumber} (no sig headers)
Grid API-->>Client: 202 {payloadToSign, requestId, expiresAt}
Client->>Client: Sign payloadToSign with session API keypair
Client->>Grid API: PATCH /customers/{id} {phoneNumber} + Grid-Wallet-Signature + Request-Id
Grid API-->>Client: 200 Updated customer (phoneNumber + SMS_OTP creds updated)
Note over Client,Grid API: Rejected – combined contact update
Client->>Grid API: PATCH /customers/{id} {email, phoneNumber}
Grid API-->>Client: 400 Bad request
%%{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 API
Note over Client,Grid API: Phone update – no SMS_OTP credentials tied
Client->>Grid API: PATCH /customers/{id} {phoneNumber}
Grid API-->>Client: 200 Updated customer (phoneNumber set)
Note over Client,Grid API: Phone update – SMS_OTP credentials tied (signed-retry)
Client->>Grid API: PATCH /customers/{id} {phoneNumber} (no sig headers)
Grid API-->>Client: 202 {payloadToSign, requestId, expiresAt}
Client->>Client: Sign payloadToSign with session API keypair
Client->>Grid API: PATCH /customers/{id} {phoneNumber} + Grid-Wallet-Signature + Request-Id
Grid API-->>Client: 200 Updated customer (phoneNumber + SMS_OTP creds updated)
Note over Client,Grid API: Rejected – combined contact update
Client->>Grid API: PATCH /customers/{id} {email, phoneNumber}
Grid API-->>Client: 400 Bad request
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
openapi/components/schemas/customers/CustomerUpdateRequest.yaml:37-45
**Schema permits what the API rejects**
The prose in `customers_{customerId}.yaml` explicitly states "A request that includes both fields is rejected," but the `CustomerUpdateRequest` schema defines `email` and `phoneNumber` as independent optional properties with no mutual-exclusivity constraint. Any OpenAPI validator or SDK generator treats the schema as the source of truth, so a client that sends `{"customerType":"INDIVIDUAL","email":"a@b.com","phoneNumber":"+14155559876"}` will pass schema validation, then receive a `400` from the server with no schema-level explanation.
Adding a `not` constraint directly in `CustomerUpdateRequest.yaml` would encode the restriction machine-readably:
```yaml
not:
required:
- email
- phoneNumber
```
Without this, the schema actively misrepresents the API's behavior to every consumer that relies on it for validation or code generation.
Reviews (4): Last reviewed commit: "Drop API version bump" | Re-trigger Greptile
Tomorrow is cool. I just need the phone number in the API for customer creation. Totally unrelated to your webdev work though. Why do you need an API version bump for this btw? Would be good to avoid that if possible |
|
oh i prolly have the wrong understanding of how the api works but i thought if you regen grid-api in webdev you need to bump the api version too? |
|
lemme also fix the greptile comments on this pr, but then we should be cool to merge it rn |
|
oh were you talking about |
- Document phone-number uniqueness (SMS_OTP) in the PATCH 409 response - Use "strict E.164 format" wording in Customer response schema for consistency Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This change is purely additive (optional phoneNumber field), so it does not warrant an info.version bump. Revert info.version to 2026-06-25 to match main; the PR now carries no version change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e0b4849 to
0d9ac4e
Compare
| phoneNumber: | ||
| type: string | ||
| pattern: '^\+[1-9]\d{1,14}$' | ||
| description: >- | ||
| Phone number for the customer in strict E.164 format. For customers with | ||
| tied Embedded Wallet internal accounts, changing this value also updates | ||
| every tied `SMS_OTP` credential across all tied Embedded Wallets. Send | ||
| phone number and email updates as separate PATCH calls. | ||
| example: '+14155551234' |
There was a problem hiding this comment.
Schema permits what the API rejects
The prose in customers_{customerId}.yaml explicitly states "A request that includes both fields is rejected," but the CustomerUpdateRequest schema defines email and phoneNumber as independent optional properties with no mutual-exclusivity constraint. Any OpenAPI validator or SDK generator treats the schema as the source of truth, so a client that sends {"customerType":"INDIVIDUAL","email":"a@b.com","phoneNumber":"+14155559876"} will pass schema validation, then receive a 400 from the server with no schema-level explanation.
Adding a not constraint directly in CustomerUpdateRequest.yaml would encode the restriction machine-readably:
not:
required:
- email
- phoneNumberWithout this, the schema actively misrepresents the API's behavior to every consumer that relies on it for validation or code generation.
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/customers/CustomerUpdateRequest.yaml
Line: 37-45
Comment:
**Schema permits what the API rejects**
The prose in `customers_{customerId}.yaml` explicitly states "A request that includes both fields is rejected," but the `CustomerUpdateRequest` schema defines `email` and `phoneNumber` as independent optional properties with no mutual-exclusivity constraint. Any OpenAPI validator or SDK generator treats the schema as the source of truth, so a client that sends `{"customerType":"INDIVIDUAL","email":"a@b.com","phoneNumber":"+14155559876"}` will pass schema validation, then receive a `400` from the server with no schema-level explanation.
Adding a `not` constraint directly in `CustomerUpdateRequest.yaml` would encode the restriction machine-readably:
```yaml
not:
required:
- email
- phoneNumber
```
Without this, the schema actively misrepresents the API's behavior to every consumer that relies on it for validation or code generation.
How can I resolve this? If you propose a fix, please make it concise.
Yeah I was just talking about the schema version in this PR. I don't think we need to bump it. |

Summary
phoneNumberto customer create, update, and response schemas.phoneNumberas strict E.164 (+plus country code and subscriber number).PATCH /customers/{customerId}docs to cover SMS OTP signed retry and to reject combinedemail+phoneNumbercontact updates.2026-06-26.Endpoint structure after this change
Create customer
Response includes the same top-level contact fields:
{ "id": "Customer:...", "customerType": "INDIVIDUAL", "email": "jane.smith@example.com", "phoneNumber": "+14155551234", "umaAddress": "$jane.smith@example.umadomain", "createdAt": "2026-06-26T12:00:00Z", "updatedAt": "2026-06-26T12:00:00Z" }Synchronous phone update
If no tied
SMS_OTPcredentials need Turnkey sync, the endpoint returns200with the updated customer andphoneNumber.Signed SMS OTP phone update
Initial call returns
202:{ "payloadToSign": "{\"organizationId\":\"org_...\",\"parameters\":{\"userId\":\"user_...\",\"userPhoneNumber\":\"+14155559876\"},\"timestampMs\":\"1775681700000\",\"type\":\"ACTIVITY_TYPE_UPDATE_USER_PHONE_NUMBER\"}", "requestId": "Request:...", "expiresAt": "2026-04-08T15:35:00Z" }The retry sends the same request body plus
Grid-Wallet-SignatureandRequest-Id; success returns200with the updated customer.Rejected combined contact update
This shape is documented as rejected; clients should send separate PATCH calls.
Validation
make buildmake lint