Skip to content

docs: document which transaction type a transfer or deposit creates - #857

Open
shreyav wants to merge 4 commits into
claude/transfer-api-deprecation-docs-6ycamqfrom
claude/transaction-type-docs-stacked
Open

docs: document which transaction type a transfer or deposit creates#857
shreyav wants to merge 4 commits into
claude/transfer-api-deprecation-docs-6ycamqfrom
claude/transaction-type-docs-stacked

Conversation

@shreyav

@shreyav shreyav commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

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 feat(spec): add INCOMING_PAYMENT.PROCESSING to the webhook contract #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; feat(spec): add INCOMING_PAYMENT.PROCESSING to the webhook contract #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

@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 Preview Aug 24, 2026 3:42pm
grid-wallet-demo Ignored Ignored Preview Aug 24, 2026 3:42pm

Request Review

@shreyav shreyav changed the title docs: document which transaction type a transfer or deposit creates docs: document which transaction type a transfer or deposit creates, and add INCOMING_PAYMENT.PROCESSING Aug 24, 2026
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

✱ Stainless preview builds for grid

No changes were made to the SDKs.


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:42:40 UTC

@shreyav
shreyav force-pushed the claude/transaction-type-docs-stacked branch from d7ea6db to f313172 Compare August 24, 2026 04:10
@shreyav shreyav changed the title docs: document which transaction type a transfer or deposit creates, and add INCOMING_PAYMENT.PROCESSING docs: document which transaction type a transfer or deposit creates Aug 24, 2026
@shreyav
shreyav marked this pull request as ready for review August 24, 2026 15:12
@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR explains how transaction types determine lifecycle and webhook handling, then carries that guidance into transaction filtering and shared terminology.

  • Adds an incoming-versus-outgoing classification table and examples.
  • Splits lifecycle webhook documentation into incoming and outgoing event families.
  • Links transaction filtering guidance to the lifecycle explanation.
  • Updates shared terminology to describe destination-based classification.

Confidence Score: 4/5

The documentation should be corrected before merging because its UMA-address row can direct integrations to use the wrong transaction type and webhook family.

The new table says every UMA-address destination is outgoing, while both the same page and the incoming-payment contract show that payments received at customer UMA addresses are incoming.

Files Needing Attention: mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx

Important Files Changed

Filename Overview
mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx Adds transaction-type and incoming-webhook guidance, but the unconditional UMA-address row contradicts the documented and specified inbound UMA case.
mintlify/payouts-and-b2b/payment-flow/list-transactions.mdx Adds filtering guidance and a valid root-relative link, while inheriting the lifecycle page's overly broad UMA classification.
mintlify/snippets/terminology.mdx Replaces perspective-based terminology with destination-based wording, though the linked detailed explanation needs to distinguish inbound customer UMA addresses.
Prompt To Fix All With AI
### Issue 1
mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx:19
**UMA destination rule misclassifies inbound payments**

When an integration classifies a payment received at a customer's UMA address, this unconditional `UMA address``OUTGOING` row conflicts with the documented and specified `INCOMING` transaction, causing the integration to filter for the wrong type or listen to the wrong webhook family.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "docs: list INCOMING_PAYMENT.PROCESSING i..." | Re-trigger Greptile

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 UMA destination rule misclassifies inbound payments

When an integration classifies a payment received at a customer's UMA address, this unconditional UMA addressOUTGOING row conflicts with the documented and specified INCOMING transaction, causing the integration to filter for the wrong type or listen to the wrong webhook family.

Knowledge Base Used: Payments, quotes, and transfers API

Prompt To Fix With AI
This is a comment left during a code review.
Path: mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx
Line: 19

Comment:
**UMA destination rule misclassifies inbound payments**

When an integration classifies a payment received at a customer's UMA address, this unconditional `UMA address``OUTGOING` row conflicts with the documented and specified `INCOMING` transaction, causing the integration to filter for the wrong type or listen to the wrong webhook family.

**Knowledge Base Used:** [Payments, quotes, and transfers API](https://app.greptile.com/lightspark/-/custom-context/knowledge-base/lightsparkdev/grid-api/-/docs/payments-and-quotes-api.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@shreyav
shreyav force-pushed the claude/transaction-type-docs-stacked branch from f313172 to d3e5c01 Compare August 24, 2026 15:21
shreyav added a commit that referenced this pull request Aug 24, 2026
…858)

> [!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:

```python
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
`PENDING` — `gen_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](https://claude.ai/code/session_01TxsyZEDjQDt97rR3kd2kxv)_

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
shreyav and others added 4 commits August 24, 2026 08:42
Nothing in the docs said whether a given transfer produces an INCOMING or
an OUTGOING transaction, and the transaction lifecycle page contained no
occurrence of the word "incoming" at all — it described only the outgoing
flow, despite INCOMING_PAYMENT webhooks existing and being referenced from
six other pages.

Added an "Incoming or outgoing" section at the top of that page with the
rule and its table. The type is keyed on the destination, regardless of
source: anything landing in an internal account is INCOMING, anything
landing in an external account or at a UMA address is OUTGOING. So a pull
from an external account is INCOMING even though the platform initiated
it, a transfer between two internal accounts is INCOMING, and a deposit
paid to an account's payment instructions is INCOMING.

This mirrors the dispatch in sparkcore's gen_convert_to_transaction and
the truth table on gen_send_op_destination_is_internal_account.

Also split the webhook event table into outgoing and incoming families.
The incoming list was missing entirely; it has no PENDING_AUTHORIZATION,
PROCESSING, or EXPIRED event, which is worth stating since those belong to
executing a quote.

Terminology said the type was "from the platform's perspective", which
does not tell a reader how to predict it, and the list-transactions type
filter gave no way to know which value to pass. Both now state the rule
and link to the table.

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

The note claimed incoming payments have no PROCESSING. That is only true
of the webhook event; the status exists.

Both incoming converters map display_status straight through, and
PROCESSING is a member of that enum. gen_convert_send_op_to_incoming_transaction
goes further and maps SENT onto PROCESSING explicitly, so a pull into an
internal account reports status PROCESSING while it is in flight even
though no INCOMING_PAYMENT.PROCESSING webhook ever fires.

Reworded to scope the claim to which events fire, and added a warning not
to infer transaction state from webhook coverage.

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

The previous note explained the absence of an INCOMING_PAYMENT.PROCESSING
event as an intentional difference between the incoming and outgoing
webhook families. It is not intentional — it is a sparkcore bug, so
documenting a rationale for it would have enshrined it.

_get_incoming_webhook_type collapses CREATED, PENDING, PROCESSING and SENT
onto INCOMING_PAYMENT.PENDING, while the outgoing equivalent maps
PROCESSING onto its own event. A pull into an internal account therefore
signals PENDING twice, and the second payload carries status PROCESSING
inside an envelope typed PENDING.

The table still lists what fires today. Dropped the rationale and replaced
it with a pointer to read status off the transaction, which stays true
either way. The PROCESSING row lands here once sparkcore emits it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TxsyZEDjQDt97rR3kd2kxv
The enum value lands in the spec one PR below this one. Adds its row to
the incoming event table this PR introduces.

Also drops the line telling readers the event name is a coarser signal
than the status it carries. That was describing a bug as guidance:
WebhookType is meant to be routable on its own, and once sparkcore emits
PROCESSING it will be.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TxsyZEDjQDt97rR3kd2kxv
@shreyav
shreyav force-pushed the claude/transaction-type-docs-stacked branch from d3e5c01 to 464ddc0 Compare August 24, 2026 15:42
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.

1 participant