Operator
Payment attempts
GET /api/payment-attempts lists recorded payment attempts, including uncertain settlements; POST …/:id/resolve records an operator verdict.
/api/payment-attemptsBearer adminTokenLists recorded payment attempts, newest first, whether settled or not. It requires the same authentication as receipts. Use it to reconcile uncertain settlements, which leave no receipt even though funds may have moved. For the whole purchase, with its backend call and receipt, read the transaction.
Query
limitintegerdefault: 50400 INPUT_INVALID.uncertainbooleandefault: falsetrue lists only attempts marked settlement-uncertain or legacy failed. Values other than true or false return 400 INPUT_INVALID.curl -s "http://localhost:8080/api/payment-attempts?uncertain=true" \
-H "Authorization: Bearer $ADMIN_TOKEN"Payment attempt
id / requestId / resourceIdstringrequiredprovider / replayKeystringrequiredstatusstringrequiredreserved, verified, settled, rejected, settlement-uncertain, or failed (legacy). The pipeline no longer writes this status.amount / currencystringrequiredpayer / payeestringexternalReferencestringrejectionReasonstringtransactionIdstringresolutionobjectmethod (reconcile or manual), actor, reason, at, and when available externalReference (unverified for manual) and public chain evidence. Later pipeline writes cannot overwrite the resolution.createdAt / updatedAtISO 8601requiredagent-commerce reconcile reads the chain evidence for you; doctor reports the count when it is nonzero.Resolve an attempt
/api/payment-attempts/{id}/resolveBearer adminTokenRecords an operator verdict, with the same rules as agent-commerce resolve: only reserved, settlement-uncertain and legacy failed attempts, once each. The admin token controls access but does not identify the operator, so the body names an actor. With AP2 enabled, the gateway also tries to consume or release the attempt's mandate hold.
Body
statussettled | rejectedrequiredactorstringrequiredreasonstringrequiredexternalReferencestringconfirmExpiredbooleanBodies over 4 KiB return 413 and malformed JSON returns 400, both in the framework's format (see Errors). Valid JSON that is not an object, an unknown field, a wrong type, another status, or a blank or overlong text field returns 400 INPUT_INVALID. None of these writes anything.
curl -s -X POST "http://localhost:8080/api/payment-attempts/attempt_7c1e…/resolve" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "content-type: application/json" \
-d '{"status":"settled","actor":"[email protected]","reason":"transfer seen in explorer","externalReference":"0x4f2c…9ab1"}'hold appears only for an attempt with an AP2 hold; with written: false, reason explains why the journal still shows it as pending. A later reconcile run can retry it when AP2 is enabled. A refused resolution returns 409 INPUT_INVALID with details.reason and details.attemptStatus:
| details.reason | Meaning |
|---|---|
already_resolved | the attempt already has a resolution, possibly from a concurrent resolver |
not_resolvable | the attempt is in a final status, or its status changed without a resolution |
too_recent | a reserved attempt younger than 15 minutes; the gateway may still be settling it |
not_expired | rejected while the recorded expiry is still in the future |
expiry_unknown | rejected without a valid recorded expiry; resend with confirmExpired: true |
An unknown attempt id is 404 RESOURCE_NOT_FOUND.