Skip to content

feat(customers): contact (email/phone) verification surface - #557

Merged
jklein24 merged 6 commits into
mainfrom
feat/striga-contact-verification
Jun 17, 2026
Merged

feat(customers): contact (email/phone) verification surface#557
jklein24 merged 6 commits into
mainfrom
feat/striga-contact-verification

Conversation

@jklein24

@jklein24 jklein24 commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

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 — contactVerification is omitted from their responses and the endpoints return 409 for 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, readOnly contactVerification object — present only when the provider requires verification.

  • New paths:

    • POST /customers/{customerId}/verify-email + /verify-email/confirm
    • POST /customers/{customerId}/verify-phone + /verify-phone/confirm

    Send endpoints (re)send the OTP; /confirm submits { code }. They return 409 when the customer's provider doesn't require contact verification.

Docs

  • Added a region-scoped step to the unregulated hosted-KYC flow (snippets/kyc/kyc-unregulated.mdx) documenting the verify-email/verify-phone send+confirm endpoints and the contactVerification status object, noting that generating a KYC link before both channels are VERIFIED returns 409.

Notes

  • Bundled spec (openapi.yaml, mintlify/openapi.yaml) regenerated; redocly lint passes.
  • Once this merges and the SDK regenerates, the webdev side drops its temporary types (raw-dict request parsing → ContactVerificationConfirmRequest; response-dict injection → contactVerification on the Customer model).

🤖 Generated with Claude Code

jklein24 added 2 commits June 6, 2026 01:42
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.
@mintlify

mintlify Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
Grid 🟢 Ready View Preview Jun 7, 2026, 12:22 AM

@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
grid-flow-builder Ignored Ignored Preview Jun 7, 2026 6:13am

Request Review

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

✱ Stainless preview builds for grid

This PR will update the grid SDKs with the following commit messages.

cli

chore(internal): regenerate SDK with no functional changes

csharp

feat(api): add contact_verification field to customer models

go

feat(api): add contact verification field to customer and webhook responses

kotlin

feat(api): add contactVerification field to Customer/BusinessCustomer/IndividualCustomer

openapi

feat(api): add contact verification endpoints and types to customers

php

feat(api): add contactVerification field to Customer/BusinessCustomer/IndividualCustomer

python

feat(api): add contact_verification field and ContactVerification type to Customer

ruby

feat(api): add contact_verification field to Customer model

typescript

feat(api): add contactVerification field to customer responses
grid-openapi studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅

grid-ruby studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ✅lint ✅test ✅

⚠️ grid-go studio · code

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

go get github.com/stainless-sdks/grid-go@a74fd86984eb639837321f07433d8ef82186f106
grid-kotlin studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ✅lint ✅test ✅

⚠️ grid-python studio · code

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

pip install https://pkg.stainless.com/s/grid-python/cc247d1194b471e90548d172778212f89f373be9/grid-0.0.1-py3-none-any.whl
⚠️ grid-csharp studio · code

Your SDK build had a failure in the build CI job, which is a regression from the base state.
generate ⚠️build ❗lint ✅test ❗

grid-php studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅lint ✅test ✅

grid-typescript studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ✅lint ✅test ✅

npm install https://pkg.stainless.com/s/grid-typescript/6798574dff4c5a4103d033507742995a6628f8ce/dist.tar.gz
⚠️ grid-cli studio · code

Your SDK build had a failure in the test CI job, which is a regression from the base state.
generate ⚠️build ⏭️lint ⏭️test ❗


This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-06-17 21:46:17 UTC

@greptile-apps

greptile-apps Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This 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 contactVerification field is omitted from their Customer objects and the new endpoints return 409 for providers with no verification requirement.

  • New schemas (ContactVerificationStatus, ContactVerification, ContactVerificationConfirmRequest) and four new endpoints (verify-email, verify-email/confirm, verify-phone, verify-phone/confirm) are added with consistent auth, error codes, and sandbox behavior.
  • Customer schema gains an optional readOnly contactVerification field; kyc-link gains a 409 response documenting the pre-KYC gate.
  • kyc-unregulated.mdx is updated with a new "Verify contact channels" step that correctly scopes the requirement to providers that mandate it.

Confidence Score: 5/5

Safe 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.

Important Files Changed

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 }"
Loading
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

Comment thread openapi/paths/customers/customers_{customerId}_verify-email.yaml
…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>
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).
@jklein24
jklein24 merged commit e56119f into main Jun 17, 2026
9 checks passed

Copy link
Copy Markdown
Contributor Author

Merge activity

@jklein24
jklein24 deleted the feat/striga-contact-verification branch June 17, 2026 21:39
pengying pushed a commit that referenced this pull request Jun 18, 2026
## Summary
- Added `FAMILY_SUPPORT` and `SALARY_PAYMENT` to the documented purpose of payment codes in the cross-currency transfers guide

These values were added to the OpenAPI schema in PR #592 (commit 460f14d) but the Mintlify documentation wasn't updated.

## Changes
**Documentation (mintlify/):**
- `mintlify/snippets/sending/cross-currency.mdx` - Added missing purpose of payment codes to match the schema

## Not updated (no changes needed)
- **Contact verification endpoints** (PR #557): Already documented in `mintlify/snippets/kyc/kyc-unregulated.mdx`
- **Stablecoin provider accounts** (PR #594): New API surface - not referenced in existing guides
- **Kotlin sample app**: Uses SDK factory method for PurposeOfPayment which auto-supports new values
- **Grid Visualizer**: Uses `'GIFT'` as example value which remains valid

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants