Skip to content

feat(spec): add INCOMING_PAYMENT.PROCESSING to the webhook contract - #858

Merged
shreyav merged 1 commit into
mainfrom
claude/incoming-processing-webhook
Aug 24, 2026
Merged

feat(spec): add INCOMING_PAYMENT.PROCESSING to the webhook contract#858
shreyav merged 1 commit into
mainfrom
claude/incoming-processing-webhook

Conversation

@shreyav

@shreyav shreyav commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Note

Bottom of a three-PR stack, based on main and mergeable on its own. #856 and #857 sit on top of it. Deliberately isolated so the sparkcore fix that depends on this enum isn't blocked behind the docs work above.

The bug

WebhookType describes itself as dot-notation that "lets consumers route purely on type without inspecting data.status".

The incoming family breaks that promise. _get_incoming_webhook_type (sparkcore/grid/webhooks/webhook_handler.py:142) collapses four states onto one event:

case (CREATED | PENDING | PROCESSING | SENT):
    return WebhookType.INCOMING_PAYMENT_DOT_PENDING

The outgoing twin 24 lines above maps PROCESSING to its own event. So a pull into an internal account fires INCOMING_PAYMENT.PENDING twice, and the second payload carries "status": "PROCESSING" inside an envelope typed PENDING — exactly the inspect-data.status case the contract says consumers should not need.

Likely why it went unnoticed: the receive-operation path (ordinary deposits) does not appear to reach PROCESSING/SENT, so the collapse was harmless. GK.GRID_INCOMING_TRANSACTION_REFACTOR then routed send operations — which do pass through those states — into this function without extending it.

This PR

Contract only. Adds INCOMING_PAYMENT.PROCESSING to WebhookType and to the IncomingPaymentWebhook type enum, mirroring the outgoing family.

sparkcore does not emit the event yet. Its WebhookType is generated from this spec, so this has to land before the emission fix can reference the new member.

Follow-up in webdev, after this merges

  1. Regenerate the grid-api Python SDK so WebhookType.INCOMING_PAYMENT_DOT_PROCESSING exists.
  2. Split PROCESSING | SENT out of the PENDING case in _get_incoming_webhook_type. SENT belongs with PROCESSING, not PENDINGgen_convert_send_op_to_incoming_transaction already maps SENT onto status PROCESSING (transaction.py:891), so that is what makes the envelope agree with its payload.

Note for whoever picks that up: it changes behavior for existing integrators, who currently receive two PENDING events and will start receiving PENDING then PROCESSING.

A smaller instance of the same collapse is out of scope here: _get_incoming_webhook_type also folds EXPIRED into INCOMING_PAYMENT.FAILED.

Validation

Check Result
make lint exit 0 — 0 errors
make build + bundle sync openapi.yaml / mintlify/openapi.yaml in sync
oasdiff 1.16.0 vs main no breaking changes — adding an enum value widens the contract

Generated by Claude Code

WebhookType describes itself as dot-notation that "lets consumers route
purely on type without inspecting data.status". The incoming family breaks
that promise: it has no PROCESSING event, so sparkcore's
_get_incoming_webhook_type collapses CREATED, PENDING, PROCESSING and SENT
onto INCOMING_PAYMENT.PENDING, while the outgoing equivalent gives
PROCESSING its own event.

A pull into an internal account therefore fires PENDING twice, and the
second payload carries status PROCESSING inside an envelope typed PENDING
— exactly the inspect-data.status case the contract says consumers should
not need.

Adds the value to WebhookType and to the IncomingPaymentWebhook type enum,
mirroring the outgoing family. Contract only: sparkcore does not emit the
event yet, and its WebhookType is generated from this spec, so this has to
land before the emission fix can reference the new member.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TxsyZEDjQDt97rR3kd2kxv
@mintlify

mintlify Bot commented Aug 24, 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 Aug 24, 2026, 4:12 AM

@vercel

vercel Bot commented Aug 24, 2026

Copy link
Copy Markdown

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

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
grid-flow-builder Ignored Ignored Aug 24, 2026 4:11am
grid-wallet-demo Ignored Ignored Aug 24, 2026 4:11am

Request Review

@github-actions

github-actions Bot commented Aug 24, 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

go

feat(api): add processing status to incoming payment webhook events

kotlin

feat(api): add INCOMING_PAYMENT_PROCESSING to IncomingPaymentWebhookEvent

openapi

feat(api): add PROCESSING event to incoming payments

php

feat(api): add PROCESSING status to IncomingPaymentWebhookEvent

python

feat(api): add INCOMING_PAYMENT.PROCESSING event type to incoming payment webhooks

ruby

feat(api): add processing status to incoming payment webhook events

typescript

feat(api): add INCOMING_PAYMENT.PROCESSING event type to webhooks
⚠️ grid-openapi studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️

⚠️ grid-ruby studio · code

Your SDK build had at least one "warning" 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@4bea1b99560e2eae637d18c71828fc4e1248640d
⚠️ grid-kotlin 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 ❗

⚠️ grid-typescript studio · conflict

Your SDK build had at least one warning diagnostic.

⚠️ 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/ac31e55c0046d3fdb2407427ff6abb0d9414f8f5/grid-0.0.1-py3-none-any.whl
⚠️ grid-php studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️lint ✅test ✅

⚠️ 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-08-24 15:51:00 UTC

@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds INCOMING_PAYMENT.PROCESSING to the shared webhook type registry and incoming-payment payload contract, preparing generated clients for a subsequent emitter change.

  • Updates both modular webhook schemas with the new event value.
  • Synchronizes the generated root and Mintlify OpenAPI bundles.
  • Keeps the shared event registry and incoming-payment schema narrowing aligned.

Confidence Score: 5/5

The PR appears safe to merge because the additive event value is consistently represented across the authoritative schemas and generated bundles.

The shared webhook registry and incoming-payment payload enum remain aligned, no additional discriminator or registry requires updating, and the generated contracts mirror the modular source.

Important Files Changed

Filename Overview
openapi/components/schemas/webhooks/WebhookType.yaml Adds the processing event to the authoritative shared webhook type registry.
openapi/components/schemas/webhooks/IncomingPaymentWebhook.yaml Adds the same event to the incoming-payment webhook's narrowed type enum.
openapi.yaml Correctly mirrors both source-schema additions in the generated bundled contract.
mintlify/openapi.yaml Keeps the Mintlify contract bundle synchronized with the generated root contract.

Reviews (1): Last reviewed commit: "feat(spec): add INCOMING_PAYMENT.PROCESS..." | Re-trigger Greptile

@shreyav
shreyav requested a review from pengying August 24, 2026 14:45
@shreyav
shreyav merged commit 6d2e6bc into main Aug 24, 2026
10 checks passed
@shreyav
shreyav deleted the claude/incoming-processing-webhook branch August 24, 2026 15:42
shreyav added a commit that referenced this pull request Aug 24, 2026
…857)

> [!NOTE]
> **Top of a three-PR stack:** #858 (spec) → #856 (transfer deprecation)
→ this. Base is `claude/transfer-api-deprecation-docs-6ycamq`, so the
diff here is only the 3 files below. Each merge retargets this
automatically.

## The gap

Nothing in the docs said whether a given transfer produces an `INCOMING`
or an `OUTGOING` transaction. Worse, the Transaction Lifecycle page
contained **zero occurrences of the word "incoming"** — it described
only the outgoing flow, despite `INCOMING_PAYMENT.*` webhooks existing
and being referenced from six other pages.

## The rule

The type is keyed on the **destination**, regardless of source:

| Destination | `type` | Webhook family |
|---|---|---|
| Internal account | `INCOMING` | `INCOMING_PAYMENT.<STATUS>` |
| External account | `OUTGOING` | `OUTGOING_PAYMENT.<STATUS>` |
| UMA address | `OUTGOING` | `OUTGOING_PAYMENT.<STATUS>` |

Consequences worth spelling out, and now spelled out:

- A pull from an external account into an internal account is
`INCOMING`, even though the platform initiated it.
- A transfer between two internal accounts is `INCOMING`.
- A deposit that lands by paying an internal account's payment
instructions is `INCOMING`, as is a payment received at a customer's UMA
address.

This mirrors the dispatch in `gen_convert_to_transaction`
(`sparkcore/grid/objects/transaction.py:159`), which routes
`EntGridReceiveOperation` to incoming unconditionally and branches a
send operation on `gen_send_op_destination_is_internal_account`. That
helper carries the same truth table in its docstring at
`transaction.py:738`. The branch is gated on
`GK.GRID_INCOMING_TRANSACTION_REFACTOR`, which is rolled out, so the
table describes current behavior for all platforms.

## Changes

- **`transaction-lifecycle.mdx`** — new **Incoming or outgoing** section
at the top, ahead of the flow sections, since the type decides which
lifecycle and webhook family apply.
- **`transaction-lifecycle.mdx`** — split the webhook event table into
outgoing and incoming families. The incoming list was missing entirely.
Its `INCOMING_PAYMENT.PROCESSING` row corresponds to the enum value
added in #858.
- **`terminology.mdx`** — said the type was "from the platform's
perspective", which does not tell a reader how to predict it. Now states
the rule.
- **`list-transactions.mdx`** — the *Filter by transaction type* section
had the reader choosing `type=INCOMING|OUTGOING` with no way to know
which their payout is. Now states the rule and links to the table.

## Two things this PR previously got wrong

Recorded because both were corrected in place and a reviewer reading
only the head would not see them:

1. An early revision claimed incoming transactions have no `PROCESSING`
**state**. False — they do; both incoming converters map
`display_status` through, and
`gen_convert_send_op_to_incoming_transaction` maps `SENT` onto
`PROCESSING` explicitly (`transaction.py:891`). Only the *webhook event*
is missing.
2. The next revision then explained that missing event as an intentional
design difference, which would have enshrined a bug. It is a sparkcore
bug; #858 adds the enum and describes the emission fix.

## Validation

- `make lint` exits 0 — 663 problems, unchanged from baseline (all
pre-existing)
- Docs-only: no files under `openapi/`, so the bundle, oasdiff, and SDK
generation are untouched by this PR
- The `#incoming-or-outgoing` anchor is linked from
`list-transactions.mdx`; the heading carries no punctuation, so the slug
is unambiguous

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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