This guide describes the financial-data release. It requires a compatible SDK
or MCP build and an API environment with structured reads enabled. Availability
on the hosted endpoint is controlled separately from this documentation.
Request permission
Configure your server’sPaymanConnect instance with
scopes: ["financial_data:read"], then use the normal
authorization and callback flow. The customer’s consent screen
shows the data permission. Use payman.forUser(customerId).startAuthorization()
to request new consent for an existing customer.
The default scope remains interact. Request both scopes only when your app
needs money tasks and data reads. Changing client configuration does not upgrade
an existing grant: the customer must consent again on the existing primary
connection, or revoke and reconnect. Adding another provider cannot broaden
permissions. Device-code pairing currently supports only interact; use the
browser authorization flow for data access.
Read through the SDK
After authorization, discover the connections the customer has shared with your app, then use exact account IDs from that connection:payman instance and IDs selected from the
returned lists. Do not substitute account labels, last-four digits, or an ID
from another connection. Account balances may be cached; a missing available
balance means unknown, not zero.
Use
user.data.read({ connectionId, read: { operation, arguments } }) for any
operation. Snapshots depend on each provider’s configured read tools. A
connection’s transactionSync describes implementation support; it does not
prove that its runtime catalog is published or that the bank is healthy.
Investments, liabilities, and statement PDFs are outside this release.
Record why a read was requested
Include optionalintent whenever the caller’s purpose is known. Hosted and local
MCP advertise it by default and ask the bot to include the known purpose without
asking the user just to populate metadata. Omitting it or sending null keeps
existing integrations working. Payman does not invent a reason when none is supplied.
String values are trimmed and must not be blank. Keep the purpose concise; do not
include credentials or the full conversation. Intent is descriptive context and
does not change permissions, tool arguments, or trigger a model call.
The SDK accepts
intent on data.start() / data.read() requests, or in the
options passed to those methods and accounts(), transactions(), and
syncTransactions(). An explicit request value overrides the options value;
intent: null explicitly omits shared context. Keep the same intent when retrying
the same requestId.
Submission responses and JSON results always include intent (or null). It is
stored encrypted with the read request and expires with the job after 24 hours.
Save runId, createdAt, intent, and the final status in your own records for
longer retention. CSV exports contain only financial rows; retain the JSON metadata
separately.
On compatible runtimes, each provider-page attempt also creates a Studio execution
trace under the read job’s session. It shows purpose, connection/account/date scope,
tool name, result counts, timing, and outcome with zero model calls. Response rows
and cursor contents are excluded. Each saved page includes executionId and
sessionId for correlation. In Studio, open the connection’s conversations and
the read’s trace; failed attempts are recorded there too.
These runtime traces follow existing execution retention and survive the Cloud
job’s 24-hour expiry. The agent’s trace storage setting still applies: DETAILED
captures this context by default, METRICS retains timing only, and NONE disables
the trace blob. Older runtimes may omit the page trace IDs until upgraded.
Keep Plaid transactions in sync
Start without a cursor. On subsequent imports, supply the checkpoint from the previous committed batch:(connectionId, accountId, id). Keep one checkpoint per grant,
connection, and account. Serialize imports for that stream, or compare and swap
the stored checkpoint when committing. A new grant or replaced bank connection
requires a full sync. Reuse the same request ID for an identical submission
retry; give each new import a fresh request ID.
Posted records can replace pending records under a different ID. Use
pendingTransactionId when supplied, and apply removals. Plaid’s exact
amountDecimal is an absolute amount in major currency units; type supplies
the credit/debit direction. Prefer this over rounded amountCents for precision.
Use the source date/datetime fields; legacy createdAt may be synthesized
from a date. These extra source fields are not certified across every provider.
The server stages the whole sync batch before returning it and restarts
pagination if the source changes during the read. Incomplete sync jobs expose
neither staged pages nor a checkpoint. Apply every returned page and persist
the final checkpoint in one database transaction.
Use MCP
Request OAuth scopefinancial_data:read and reauthorize your hosted MCP
connection. Read-only grants expose payman_connection_status,
payman_read_data, and payman_read_result, with money and approval tools
omitted. The API feature flag must also be enabled.
- Call
payman_connection_statusto discover adeploymentId. - Call
payman_read_datawithlist_accountsto discover exact account IDs. - Poll
payman_read_resultwith itsrunId. - Submit a transaction read using the same deployment and chosen account.
structuredContent as well as text. payman_read_result returns
one saved page: data.data contains its records and data.fetchedAt its retrieval
time. Follow nextPage by passing page on the next call. Retrieve every saved
page before committing a sync checkpoint, even when the job is already completed.
For the local stdio package, set PAYMAN_SCOPES=financial_data:read, restart it,
and reconnect through payman_connect. Local discovery uses
payman_data_connections and local read input uses connectionId instead of
deploymentId. These identify the same connection. The SDK uses connectionId
too. See MCP setup for building and configuring the local package.
If a hosted client always requests only interact and does not allow scope
selection, use the configurable local MCP build. Asking for raw data in chat
does not change an OAuth grant’s permissions.
Jobs, limits, and recovery
For manual polling, calldata.start(request), then data.result(runId) or
data.wait(runId). The HTTP equivalents are:
HTTP read input uses
deploymentId. Confidential clients send
x-paygent-connect-app-key and x-paygent-connect-grant. Public OAuth clients
send their token in x-paygent-connect-grant without an app key. The hosted
/mcp endpoint uses Authorization: Bearer instead. See the API reference for
the request schemas. A completed snapshot can also be requested with
?format=csv; check x-payman-read-state and x-payman-rows-complete before
using it. Change feeds require JSON.
data.read() and the convenience methods throw FinancialDataError when a job
is incomplete. Inspect error.result; do not treat a partial export as complete.
read_timeout and read_aborted carry runId so you can resume polling without
submitting a new job. read_submission_unknown carries requestId; retry the
same input with that ID. These data methods throw, unlike money-task run()’s
result union.
If a sync batch exceeds the bounds, do not save a partial cursor. Try a larger
page size up to 500 through data.read(). If it still exceeds the limit, the
integration needs a larger export path before that batch can be committed.
rowsComplete=true means the requested provider pagination finished. It does
not certify that the bank supplied all historical records. historyComplete
remains false; sourceUpdatedAt remains null until independently verified.
fetchedAt is retrieval time, not the bank’s refresh time. Reconcile against
statements before relying on an import as complete books.
