List transfer events
List transfer events using the standard get_all query format (search.*, sort, limit, offset, stack).
Authorization
BearerAuth JWT authentication token
In: header
Query Parameters
Filter by transfer ID
Filter by event type (DSL-defined events, or 'status_change' for status change records). Only 'init' and 'complete' are system events.
Filter by from_status
Filter by to_status
Filter by creator user ID
Filter by error code
Filter by error message
Stack response data by a field (e.g. stack=event_type or stack=created_at[YYYY-MM-DD])
Sort field (prefix with - for descending). Example: -created_at or event_type
Number of items per page
Starting position for pagination
JSON filter object, parsed by the shared parser. Malformed JSON is refused with 400.
Free-text search over the virtual full-row text expression, resolved by the shared parser's ExtractTextSearch. Bare search._text implies the like operator; the operator suffixes accepted are .eq, .like, .ilike, .contains, .start_with and .end_with. More than one of them in a single request is refused with 400, as is any other suffix.
Header Parameters
Language preference for the response
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/v1/transfers/events"{
"data": [
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"transfer_id": "d4a2d8dd-7def-4545-a062-761683b9aa05",
"created_at": "2019-08-24T14:15:22Z",
"created_by": "ee824cad-d7a6-4f48-87dc-e8461a9201c4",
"event_type": "string",
"from_status": "string",
"to_status": "string",
"payload": {},
"context": {},
"dsl_trace_id": "string",
"error_code": "string",
"error_message": "string"
}
],
"total": 0,
"total_unfiltered": 0,
"has_more": true,
"keys": [
"string"
]
}{
"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"
}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.
NOT IMPLEMENTED, AND UNAUTHENTICATED. The handler ignores the request entirely and answers 501 with a bare {"error": "Not implemented yet"} map — not the apireply envelope, and note the key is error rather than message. It is also the ONLY route in this module registered with a bare HandlerFunc rather than auth.WrapWithMiddlewares: no bearer token, no RBAC check, no licence check, and no signature verification of the body. It can therefore never answer 403. THE 501 IS NOT THE ONLY ANSWER. auth.RateLimitMiddleware is on the root router ahead of the route and refuses requests before the stub runs, whenever rate_limits.rate_limits_switcher is on: - 429 WITH NO TOKEN AT ALL. The anonymous branch increments rate_limit:ip_global:<ip>:POST:/v1/kyt/webhook and answers 429 both past the global IP limit and when the increment itself fails — the ordinary shape of a provider retrying its callbacks from one address. - 401 WITH A TOKEN. The limiter parses the Authorization header itself via checkAuthorization, ahead of auth.Middleware. A valid token takes its authenticated branch into findMatchingEndpoint, which returns errs.New('permission denied').WithCode(401) when no api_permission row matches the method and path — and a route registered bare has no such row. errs.New accepts a plain string as well as a MsgCode and stores it as AppError.Key, which is what MachineCode() returns, so code is NOT empty — it is the same free-text sentence as message. Matchable, but not a dotted key and never translated. - 500 WITH A TOKEN. The same authenticated path answers 500 common.server_error when the limiter's own lookups fail (fetchUserRoles, cacheUserLimits, or findMatchingEndpoint's key search, all WithCode(500)); handleRateLimitError's default branch writes that WITHOUT the AppError, so the specific keys are discarded. With the switcher off the limiter is not in the chain and none of those three can happen. The 503 below comes from a different middleware and is reachable either way. Restrict the route at the ingress if it is exposed at all — the application does not. The path also sits outside the module's own prefix: /v1/kyt/webhook, not /v1/transfers/...