List transfer events for a single transfer
Lists the DSL events that have ALREADY EXECUTED on this transfer. IT IS NOT A CONVENIENCE ALIAS OF GET /v1/transfers/events, and it does not use the shared get_all envelope. The body is {transfer_id, events[]} with no data, total, keys or pagination, and no query parameter is read — Accept-Language is still honoured, since the error envelopes are localised like every other route's. Only successfully executed events appear; status_change rows are excluded. The list is sorted oldest-first by created_at.
Authorization
BearerAuth JWT authentication token
In: header
Path Parameters
Transfer UUID
transfer_id path parameter
Header Parameters
Language preference for the response
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/v1/transfers/string/events"{
"transfer_id": "d4a2d8dd-7def-4545-a062-761683b9aa05",
"events": [
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"key": "string",
"name": "string",
"description": "string",
"status": "executed",
"can_rerun": true,
"created_at": "2019-08-24T14:15:22Z",
"context": {}
}
]
}{
"status": 0,
"message": "string",
"code": "transfers_m.account_not_found",
"class": "validation",
"retryable": false,
"details": [
{
"field": "amount",
"rule": "gt",
"param": "0",
"message": "amount must be greater than 0"
}
]
}{
"status": 0,
"message": "string",
"code": "transfers_m.account_not_found",
"class": "validation",
"retryable": false,
"details": [
{
"field": "amount",
"rule": "gt",
"param": "0",
"message": "amount must be greater than 0"
}
]
}{
"status": 0,
"message": "string",
"code": "transfers_m.account_not_found",
"class": "validation",
"retryable": false,
"details": [
{
"field": "amount",
"rule": "gt",
"param": "0",
"message": "amount must be greater than 0"
}
]
}{
"status": 0,
"message": "string",
"code": "transfers_m.account_not_found",
"class": "validation",
"retryable": false,
"details": [
{
"field": "amount",
"rule": "gt",
"param": "0",
"message": "amount must be greater than 0"
}
]
}{
"status": 429,
"message": "Rate limit exceeded",
"code": "rate_limits_m.exceeded",
"class": "temporary",
"retryable": true
}{
"status": 0,
"message": "string",
"code": "transfers_m.account_not_found",
"class": "validation",
"retryable": false,
"details": [
{
"field": "amount",
"rule": "gt",
"param": "0",
"message": "amount must be greater than 0"
}
]
}{
"overall_status": "unhealthy",
"message": "Service is shutting down",
"timestamp": "2026-08-27T15:04:05Z"
}Executes one DSL event on a transfer. ANSWERS 200, NOT 201, and the body is {transfer, result, question?} — the transfer in the minor-units shape, the raw DSL result, and a question member only for a QUESTION outcome. THE REQUEST BODY IS A FREE-FORM JSON OBJECT. The handler decodes it into map[string]any and passes it through; a structured body with dry_run, force, reason or a nested payload wrapper is read by nothing. Payment rail facts supplied by the caller are ignored as well: they come from the rail. 400 covers an unparseable transfer_id, a missing event_type, an undecodable body, a terminal transfer (transfers_m.cannot_execute_event_terminal_transfer), a repeat of an event the product does not allow to repeat (transfers_m.cannot_execute_same_event_consecutive) and a row that is not a payment — a beneficiary's row, the receiving company's record of an on-us credit linked to the payment through parent_id, is refused with transfers_m.cannot_execute_event_on_beneficiary_row, whose details name the payment the event belongs to under the transfer_id field. THE LIST OF STATUSES BELOW IS NOT CLOSED. runTransferEvent propagates a DSL action's own *errs.AppError unchanged, so an error raised inside a call{} interface or a gl-batch surfaces with whatever status it carried. The three that a gl-batch reaches through the ledgers module are declared below: 402 ledgers.insufficient_funds against an underfunded ledger, 409 ledgers.ledger_inactive / ledgers.ledger_closed from EnsureLedgerPostable on the journal path, and 422 from the transit-ledger currency guards and from the tariff service a tariff{} step calls. A product that calls another interface can surface a status none of them lists. Only a non-AppError failure is flattened to 500 (dsl_m.execution_failed — the DSL catalogue, not the transfers one). An unknown transfer_id never answers 404. The RBAC check runs first, so it is normally a 403; a caller who passes that check reaches loadTransferForEvent, which wraps the storage miss in errs.MsgDatabaseError with no code and so answers 500.
NOT IMPLEMENTED. GetTransferEvent ignores the request entirely and answers 501 with a bare {"error": "Not implemented yet"} map — not the apireply envelope, and the key is error rather than message. The 501 is not unconditional: the route goes through auth.WrapWithMiddlewares, so a missing, malformed or expired bearer token is refused with 401, and an RBAC or licence denial with 403, before the stub body is ever produced.