feat(customers): contact (email/phone) verification surface#557
feat(customers): contact (email/phone) verification surface#557jklein24 wants to merge 6 commits into
Conversation
Adds a ContactVerification object on the Customer (present only when the
customer's payment provider requires email+phone verification, e.g. EU/Striga)
and four endpoints to drive it:
- POST /customers/{customerId}/verify-email (send/resend)
- POST /customers/{customerId}/verify-email/confirm (submit code)
- POST /customers/{customerId}/verify-phone (send/resend)
- POST /customers/{customerId}/verify-phone/confirm (submit code)
Non-EU customers are unaffected: contactVerification is omitted from their
responses and the endpoints return 409 for providers with no such requirement.
Starting point for AT-4657 (Striga EU). See design:
docs/plans/2026-06-06-striga-contact-verification-design.md (webdev).
…flow For regions that require it (e.g. EU), the customer must verify email and phone before a KYC link can be generated. Adds a region-scoped step to the unregulated hosted-KYC flow describing the verify-email/verify-phone send+confirm endpoints and the contactVerification status object.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
✱ Stainless preview builds for gridThis PR will update the cli csharp go kotlin openapi php python ruby typescript Edit this comment to update them. They will appear in their respective SDK's changelogs. ✅ grid-php studio · code · diff
✅ grid-cli studio · code · diff
✅ grid-csharp studio · code · diff
✅ grid-openapi studio · code · diff
✅ grid-python studio · code · diff
✅ grid-ruby studio · code · diff
✅ grid-typescript studio · code · diff
✅ grid-go studio · code · diff
✅ grid-kotlin studio · code · diff
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push. |
Greptile SummaryThis PR adds the contact verification surface to the Grid customer API, enabling EU customers (e.g. Striga) to verify their email and phone via OTP before proceeding to KYC. Non-EU customers are unaffected — the
Confidence Score: 5/5Safe to merge — the change is additive spec-only work that does not affect existing customers or endpoints. All new paths follow existing API patterns (auth, error codes, sandbox notes); the Customer schema extension is optional and readOnly; previously flagged issues have been addressed. The two remaining comments are documentation phrasing nits that don't affect runtime behavior. The confirm-endpoint descriptions and the kyc-link 409 description have minor wording that assumes both email and phone channels are always required, but this won't cause a functional regression.
|
| Filename | Overview |
|---|---|
| openapi/components/schemas/customers/ContactVerification.yaml | New schema for email/phone verification state; OAS 3.1 $ref+sibling description is valid; schema correctly models optional per-channel presence. |
| openapi/components/schemas/customers/ContactVerificationStatus.yaml | New PENDING/VERIFIED enum for a single verification channel; straightforward and correct. |
| openapi/components/schemas/customers/ContactVerificationConfirmRequest.yaml | New request body with required code string; clean and minimal. |
| openapi/components/schemas/customers/Customer.yaml | Adds optional readOnly contactVerification field via allOf pattern; correctly matches the established pattern for object-ref properties. |
| openapi/paths/customers/customers_{customerId}_verify-email.yaml | New send-email endpoint; 204/401/404/409/500 responses all present; sandbox note included. |
| openapi/paths/customers/customers_{customerId}_verify-email_confirm.yaml | New confirm-email endpoint; returns full Customer on 200; description says 'Both email and phone must be VERIFIED' which is inaccurate for single-channel providers per the ContactVerification schema. |
| openapi/paths/customers/customers_{customerId}_verify-phone.yaml | New send-phone endpoint; mirrors verify-email structure; responses complete. |
| openapi/paths/customers/customers_{customerId}_verify-phone_confirm.yaml | New confirm-phone endpoint; same 'Both email and phone must be VERIFIED' inaccuracy as the email confirm endpoint. |
| openapi/paths/customers/customers_{customerId}_kyc-link.yaml | Adds 409 response for unverified contact channels; 409 description hardcodes 'email and phone' rather than 'all required channels,' inconsistent with the ContactVerification schema's single-channel support. |
| mintlify/snippets/kyc/kyc-unregulated.mdx | Adds contact-verification step to the hosted and embedded KYC flows; correctly phrases the requirement as 'every present channel' rather than hardcoding both channels. |
Sequence Diagram
sequenceDiagram
participant I as Integrator
participant G as Grid API
I->>G: POST /customers (create customer)
G-->>I: "Customer { contactVerification: { email: PENDING, phone: PENDING } }"
note over I,G: EU / Striga provider only
I->>G: "POST /customers/{id}/verify-email"
G-->>I: 204 (code sent)
I->>G: "POST /customers/{id}/verify-email/confirm { code }"
G-->>I: "200 Customer { email: VERIFIED, phone: PENDING }"
I->>G: "POST /customers/{id}/verify-phone"
G-->>I: 204 (code sent)
I->>G: "POST /customers/{id}/verify-phone/confirm { code }"
G-->>I: "200 Customer { email: VERIFIED, phone: VERIFIED }"
I->>G: "POST /customers/{id}/kyc-link"
G-->>I: "200 { kycUrl, expiresAt }"
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
openapi/paths/customers/customers_{customerId}_verify-email_confirm.yaml:14
**"Both email and phone" hardcodes a two-channel assumption**
The `ContactVerification` schema explicitly states that "A provider may require both email and phone, just one of them, or — when the object is absent — neither." If a provider requires only email, the `phone` property won't appear in the object and the customer can proceed to KYC after verifying email alone. The phrase "Both email and phone must be `VERIFIED`" creates a false invariant that contradicts the schema — a reader following this description would incorrectly block KYC for single-channel providers.
The same wording appears in the `verify-phone/confirm` description (line 14). The `kyc-unregulated.mdx` snippet gets this right: "Generating the KYC link before every present channel is `VERIFIED` returns `409`." The confirm-endpoint descriptions should match that phrasing — something like "Every channel present in `contactVerification` must reach `VERIFIED` before the customer can begin KYC."
### Issue 2 of 2
openapi/paths/customers/customers_{customerId}_kyc-link.yaml:62-67
**kyc-link 409 description assumes both channels are always required**
The description says "email and phone are not both `VERIFIED` yet," implying the provider always requires two channels. The `ContactVerification` schema explicitly supports single-channel providers where only `email` or only `phone` may be present. A provider that mandates email-only verification would still fire this 409 if just email isn't verified — but the description leaves an integrator thinking they need to verify phone too.
```suggestion
'409':
description: >-
The customer's payment provider requires contact verification and not all
required contact channels are `VERIFIED` yet. Complete contact
verification (see `verify-email` and `verify-phone`) before generating a
KYC link.
```
Reviews (6): Last reviewed commit: "Make contactVerification channels indepe..." | Re-trigger Greptile
…on endpoints All four new verify-email/verify-phone (send + confirm) paths now document the '500' response that every comparable customer endpoint includes, and the confirm endpoints' '409' descriptions now cover the "already verified" case to match the send endpoints. Regenerated the bundled openapi.yaml and mintlify/openapi.yaml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Direct API flow Adds the Contact Verification tag to the root tags array so it is declared like every other operation group, and adds a note to the Direct API onboarding (KYC) flow clarifying that EU customers must verify email/phone before submitting for verification. Regenerated the bundled openapi.yaml and mintlify/openapi.yaml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The kyc-link path was missing the 409 response that the docs and the contact verification feature describe: generating a KYC link before both email and phone are VERIFIED returns 409. Added it so the spec and docs stay in sync and SDK generators produce a handler for the case. Regenerated the bundled artifacts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
358b971 to
b58b3d4
Compare
A provider may require email verification, phone verification, both, or neither. Drop the required:[email,phone] constraint so each channel is reported independently — only the channels a provider actually requires are present on the object (Striga requires both, but the API no longer assumes that). Update docs accordingly (each present channel must be VERIFIED; verify-email/verify-phone 409 for a channel the provider doesn't require).
Summary
Adds the contact verification surface to the Grid customer API: email/phone OTP verification, required only for customers whose payment provider mandates it (e.g. EU customers via Striga). Non-EU customers are unaffected —
contactVerificationis omitted from their responses and the endpoints return409for providers with no such requirement.This is the API-spec half of AT-5447; the sparkcore implementation is in webdev PRs #28352 (interface + client) and #28353 (impl + handlers).
Spec changes
New schemas (
components/schemas/customers/):ContactVerificationStatus(PENDING/VERIFIED),ContactVerification({ email, phone }),ContactVerificationConfirmRequest({ code }).Customer: optional, readOnlycontactVerificationobject — present only when the provider requires verification.New paths:
POST /customers/{customerId}/verify-email+/verify-email/confirmPOST /customers/{customerId}/verify-phone+/verify-phone/confirmSend endpoints (re)send the OTP;
/confirmsubmits{ code }. They return409when the customer's provider doesn't require contact verification.Docs
snippets/kyc/kyc-unregulated.mdx) documenting the verify-email/verify-phone send+confirm endpoints and thecontactVerificationstatus object, noting that generating a KYC link before both channels areVERIFIEDreturns409.Notes
openapi.yaml,mintlify/openapi.yaml) regenerated; redocly lint passes.ContactVerificationConfirmRequest; response-dict injection →contactVerificationon theCustomermodel).🤖 Generated with Claude Code