post_admin_stripe_backfill
Calls POST /api/admin/stripe-backfill as the signed-in person · REST reference
Import historical Stripe charges the ledger never saw
Walks the configured Stripe account's PaymentIntents and records each eligible one in the ledger. A PaymentIntent that did not succeed, was fully or partly refunded, is disputed or received nothing is reported as skipped with a reason. Platform staff only (requirePlatformStaff); there is no dedicated backfill permission. Unlike the factory reset it needs no emailed token: a committed run is additive and idempotent (the DAL MERGEs on the PaymentIntent id), so a repeat committed run reports payments the ledger already holds as alreadyPresent. ENVIRONMENT-GATED to local and staging by the same allowlist the factory reset uses, and the tier is the FIRST thing checked - a production tier answers 403 before the Stripe key is looked at. DEFAULTS TO A DRY RUN. dryRun is true unless the body sets it to exactly false, so omitting the flag - or sending any other value - reports what WOULD be imported and writes nothing. A dry run does not consult the ledger, so every eligible PaymentIntent is reported as imported, including ones the ledger already holds. Committing credits the voucher pool.
Arguments
| Argument | Type | Description | |
|---|---|---|---|
bodyJSON | object | The request body, as a JSON object. Its fields are listed below. |
Fields of body
| Field | Type | Description | |
|---|---|---|---|
dryRun | boolean | Only the literal false commits. Absent, null or any other value is a dry run, and this field is NOT range-validated the way the three below are. | |
limit | integer | Cap on PaymentIntents scanned. Non-negative finite number when supplied. | |
since | integer | Lower bound on the charge date, in epoch MILLISECONDS. Must be a non-negative finite number when supplied. | |
until | integer | Upper bound on the charge date, in epoch MILLISECONDS. Must be a non-negative finite number when supplied. |
Example call
A client sends this over the MCP session; Claude and other agents build it for you. The values are placeholders — the shape is real.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "post_admin_stripe_backfill",
"arguments": {
"body": {
"dryRun": true,
"limit": 1,
"since": 1,
"until": 1
}
}
}
}Result
On success (HTTP 200) the tool returns one text item holding the API's JSON, pretty-printed.
The run finished. A dry run and a committed run return the same shape.
{
"alreadyPresent": 1,
"dryRun": true,
"failed": 1,
"imported": 1,
"importedAmount": 1,
"outcomes": [
{
"amount": 1,
"currency": "string",
"occurredAt": 1,
"paymentIntentId": "string",
"reason": "string",
"status": "imported"
}
],
"scanned": 1,
"skipped": 1
}Fields
| Field | Type | Description |
|---|---|---|
alreadyPresent | integer | Already in the ledger, so nothing was written for them. Always 0 on a dry run, which does not consult the ledger. |
dryRun | boolean | True for a dry run, which writes nothing; false for a committed run. |
failed | integer | |
imported | integer | |
importedAmount | integer | CENTS actually added to the ledger by this run. Always 0 on a dry run. |
outcomes | array of object | One entry per PaymentIntent considered. |
outcomes[].amount | integer | CENTS. |
outcomes[].currency | string | |
outcomes[].occurredAt | integer | Epoch milliseconds. |
outcomes[].paymentIntentId | string | |
outcomes[].reason | string | Present on skipped and failed outcomes, and on every imported outcome of a dry run ("dry run — nothing written"). |
outcomes[].status | "imported" | "alreadyPresent" | "skipped" | "failed" | |
scanned | integer | |
skipped | integer |
When it fails
A failed call returns isError: true with the API's message as text. A change the person declines, or lets time out, also comes back this way and nothing changes.
400since, until or limit was supplied and is not a non-negative finite number.
403Either the caller is not platform staff, or the environment forbids the backfill entirely (production always does, and so does any tier string outside the local/staging allowlist). Both answer 403 and the body is the only way to tell them apart, exactly as on the factory-reset routes.
What the model is shown
This is the tool exactly as the server lists it in tools/list. The description starts with the REST call it makes.
{
"name": "post_admin_stripe_backfill",
"description": "POST /api/admin/stripe-backfill — Walks the configured Stripe account's PaymentIntents and records each eligible one in the ledger. A PaymentIntent that did not succeed, was fully or partly refunded, is disputed or received nothing is reported as `skipped` with a `reason`. Platform staff only (`requirePlatformStaff`); there is no dedicated backfill permission. Unlike the factory reset it needs no emailed token: a committed run is additive and idempotent (the DAL MERGEs on the PaymentIntent id), so a repeat committed run reports payments the ledger already holds as `alreadyPresent`. ENVIRONMENT-GATED to local and staging by the same allowlist the factory reset uses, and the tier is the FIRST thing checked - a production tier answers 403 before the Stripe key is looked at. DEFAULTS TO A DRY RUN. `dryRun` is true unless the body sets it to exactly `false`, so omitting the flag - or sending any other value - reports what WOULD be imported and writes nothing. A dry run does not consult the ledger, so every eligible PaymentIntent is reported as `imported`, including ones the ledger already holds. Committing credits the voucher pool.\n\nThis changes data. The person is asked to confirm it before it runs, and nothing happens if they decline.",
"inputSchema": {
"type": "object",
"properties": {
"body": {
"type": "string",
"description": "The JSON request body: { dryRun: boolean (Only the literal `false` commits.); limit: integer (Cap on PaymentIntents scanned.); since: integer (Lower bound on the charge date, in epoch MILLISECONDS.); until: integer (Upper bound on the charge date, in epoch MILLISECONDS.) }"
}
}
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": false
}
}