Skip to main content
Version: 1.7.0

Lyriq Connector API

What is the Lyriq Connector?​

The Lyriq Connector is the HTTPS API through which a bank takes part in the Lyriq tokenised-money network. It is the only system your bank's IT estate integrates with: your applications call it to register customer and treasury accounts, move funds within your bank and to other banks, issue and redeem assets, manage limits and approvals, read balances and interbank positions, and subscribe to event notifications.

Everything behind the API (policy evaluation, approval orchestration, ledger signing and submission, settlement and reconciliation) is run by the platform. Your bank sees one consistent API: requests are authenticated with a platform-issued token, always act for exactly one bank, and never return another bank's resources.

What banks can do through this API​

  • Complete onboarding through /v1/onboarding-cases: a bank admin registers staff members, machine-to-machine clients, signing and encryption keys and the webhook endpoint, then submits the configuration for operator review.
  • Register accounts with POST /v1/accounts, using your core banking system (CBS) identifier as external_account_id (ISO 20022 Max34Text: 1 to 34 characters), an asset_id, and an account type (CUSTOMER or TREASURY). Internal holding and issuance accounts are managed by the platform and are not exposed.
  • Transfer assets with POST /v1/transfers, choosing a transfer_kind (INTERBANK to a known account at another bank, BANK_ROUTED to another bank identified by bank and currency; see the Transfers section for the kinds currently available).
  • Approve work through the reviews resource when policy requires human sign-off (currently every transfer waits in PENDING_REVIEW for one checker approval).
  • Read redemptions with GET /v1/redemptions; settlement-cycle redemptions are queued, batched into a cycle, locked at cutoff, and finalised once cash settles offline.
  • Create redemption policies with POST /v1/redemption-policies; policy is activated and automatically run based on set trigger.
  • Follow settlement cycles of settlement-cycle-backed assets your bank issues (cycles are visible to the issuer only); the platform opens, locks, settles and closes cycles automatically.
  • Read balances and positions: account balances under /v1/balances, your holdings of other banks' assets under /v1/exposures and, as an issuer, what you owe each holder under /v1/liabilities.
  • Configure limits: a mint limit caps how much of an asset its issuer can create; an exposure limit is a cap a holder bank sets on itself: the most of one issuer's asset it will hold at any one time.
  • Receive notifications by registering HTTPS webhooks with rotating signing secrets, and inspect delivery history under /v1/webhooks/deliveries.
  • Submit ISO 20022 messages (pacs.009) to /v1/iso20022/messages as an alternative entry point for supported flows.

Making requests​

Base URL. Every path in this reference is relative to your deployment's Lyriq Connector URL, shown as the server URL of this reference.

Authentication. Every request carries Authorization: Bearer <access token>, where the token is issued by the platform IAM. See the Authentication section.

Bank scoping. Each request acts for one bank, taken from the token's bank_memberships claim. If the token has several memberships, choose one with the x-dan-bank-id header. Resources of other banks are never returned. OAuth scopes grant coarse capabilities (for example connector:transfers:create); each operation in this reference states the scope it requires.

Media type (JSON:API). Request and response bodies use JSON:API 1.1 with the media type application/vnd.api+json:

  • POST and PUT requests must send Content-Type: application/vnd.api+json exactly, without parameters such as charset; otherwise the response is 415 Unsupported Media Type.
  • If you send an Accept header, it must list application/vnd.api+json; a header such as Accept: */* (the default of many HTTP clients, including curl) gets 406 Not Acceptable. Either send Accept: application/vnd.api+json or omit the header.
  • A request body is a document with a top-level data member holding type and attributes. A single resource is returned as data: \{type, id, attributes, relationships, links\}; a collection as data: [...] plus pagination links. Related resources are linked through relationships.<name>.links.related.
  • The include (compound documents) and fields[<type>] (sparse fieldsets) parameters are accepted but not applied yet: responses never contain included and always contain every attribute.

Identifiers and formats. Operation, transfer and most other resource ids are UUIDs. Timestamps are RFC 3339 date-times in UTC. Amounts are strings holding an integer in the asset's smallest unit, with a separate scale giving the number of decimal places (value "100000" with scale 2 is 1000.00).

Asynchronous mutations and operations​

Requests that change state (creating a transfer, registering an account, updating a limit, changing a webhook, cancelling, and so on) are workflow mutations. They are validated, recorded, and answered with 202 Accepted before the work is done. The response body identifies a new operation, the platform's record of your request:

{
"jsonapi": { "version": "1.1" },
"data": {
"type": "operations",
"id": "01927c3e-8b4a-7d21-9f3e-5a6b7c8d9e10",
"attributes": { "resource_family": "transfers", "state": "ACCEPTED" },
"relationships": {
"resource": {
"data": { "type": "transfers", "id": "01927c3e-8b4a-7d21-9f3e-5a6b7c8d9e11" },
"links": { "related": "/v1/transfers/01927c3e-8b4a-7d21-9f3e-5a6b7c8d9e11" }
}
},
"links": { "self": "/v1/operations/01927c3e-8b4a-7d21-9f3e-5a6b7c8d9e10" }
}
}

202 Accepted means recorded, not done: the request can still be rejected by policy or a reviewer, fail during execution, or be cancelled. To learn the outcome, poll GET /v1/operations/{operation_id} until state is terminal:

Terminal stateMeaning
SUCCEEDEDThe request took effect.
REJECTEDThe request was refused; see result_code and result_message.
FAILEDExecution failed; see result_code and result_message.
CANCELLEDThe request was cancelled before it took effect.
EXPIREDA required decision was not made in time.

Non-terminal states are ACCEPTED, PROCESSING, PENDING_REVIEW (waiting for approval), AWAITING_SCREENING (waiting for the beneficiary bank's screening decision), PENDING_COMMITS (waiting for ledger finality) and MANUAL_REVIEW (an operator must reconcile the outcome; do not resubmit). See the OperationState schema for the full list.

A polling loop, with a growing delay between attempts:

OP=01927c3e-8b4a-7d21-9f3e-5a6b7c8d9e10
delay=1
while :; do
state=$(curl -sS "$CONNECTOR_URL/v1/operations/$OP" \
--header "Authorization: Bearer $TOKEN" \
--header "Accept: application/vnd.api+json" | jq -r '.data.attributes.state')
echo "state=$state"
case "$state" in
SUCCEEDED|REJECTED|FAILED|CANCELLED|EXPIRED) break ;;
esac
sleep "$delay"; delay=$(( delay < 30 ? delay * 2 : 30 ))
done
state=PROCESSING
state=PROCESSING
state=SUCCEEDED

Instead of polling, you can subscribe a webhook to operation.updated events. An operation can be cancelled while it has not yet reached the ledger with POST /v1/operations/{operation_id}/cancel.

Some requests are not workflow mutations and answer synchronously (for example reads, POST /v1/transfers/preflight, and bank onboarding steps); their descriptions say so.

Idempotency and retries​

Every workflow mutation requires an Idempotency-Key header: a string of 1 to 128 visible ASCII characters that you generate, ideally a fresh UUID per request. A missing key gives 400 Bad Request.

  • Same key, same body: once the first request has been accepted, the original 202 Accepted document is returned again with the same operation id, and nothing is executed twice. This makes it safe to retry after a timeout or a dropped connection.
  • Same key, different body: 409 Conflict with code IDEMPOTENCY_CONFLICT.
  • Same key while the first request is still being accepted: 409 Conflict with code IDEMPOTENCY_PENDING; retry after a short delay.
  • Scope: keys are remembered per bank, per calling client, per HTTP method and per endpoint path template, and only the body is compared. Path parameters are not compared, so use a new key for every distinct request.
  • Retention: a key is remembered for at least 72 hours.
  • After an error: if a request fails after its key was recorded (a business 4xx or a 5xx), the key stays reserved; the same key and body get IDEMPOTENCY_PENDING for up to 5 minutes and are then processed again. If you change the body, use a new key.

Pagination​

Collection endpoints use cursor pagination:

  • page[size] sets the page size: 1 to 200, default 20. A value outside that range gives 400 Bad Request.
  • The first page is returned when page[cursor] is absent. Each response has links.self and, when more results exist, links.next, which repeats your query with the next cursor. Follow links.next until it is absent.
  • Cursors are opaque; do not build or parse them. Only forward paging is supported (links.prev is not returned).
  • The sort order is stated per endpoint (for example, operations are listed oldest first).
"links": {
"self": "/v1/operations?page[size]=50",
"next": "/v1/operations?page%5Bsize%5D=50&page%5Bcursor%5D=CURSOR_FROM_LINKS_NEXT"
}

Errors​

Failed JSON requests return a JSON:API error document with the matching HTTP status. Branch on the status and on code when present; title and detail are for humans and their wording can change. source points at the offending body member (pointer), query or path parameter (parameter) or header (header) when it can be identified.

{
"jsonapi": { "version": "1.1" },
"errors": [
{
"status": "400",
"title": "Field format invalid",
"code": "INVALID_FIELD_FORMAT",
"detail": "/data/attributes/reason_code must not be empty",
"source": { "pointer": "/data/attributes/reason_code" }
}
]
}
StatusWhenRetry?
400Malformed JSON, invalid parameter or field, missing Idempotency-KeyNo; fix the request
401Missing, malformed, expired or foreign token (AUTHENTICATION_REQUIRED)After getting a new token
403Missing scope or bank membership, wrong x-dan-bank-id, suspended or terminated bankNo
404Resource does not exist or is not visible to your bankNo
406Accept header does not list application/vnd.api+jsonNo; fix the header
409Idempotency conflict or pending request, or a state conflictDepends on code
415Content-Type is not exactly application/vnd.api+jsonNo; fix the header
422Body does not match the expected shape, or breaks a business ruleNo; fix the request
500, 502Unexpected platform errorYes, with the same Idempotency-Key and body (it may answer IDEMPOTENCY_PENDING for up to 5 minutes first)
503Network halted or read-only (OUTBOUND_HALTED, READ_ONLY), state unknown, or a dependency unavailableYes, later, with backoff

While the network is halted or read-only, read endpoints keep working and mutations return 503. No Retry-After header is sent; use exponential backoff. ISO 20022 endpoints return errors as RFC 7807 application/problem+xml documents instead; see their descriptions.

Authentication​

Access token (a JWT) issued by the platform IAM, sent as Authorization: Bearer <token>. Required on every route. Bank-facing tokens carry a bank_memberships claim whose bank_id entries determine which bank a request acts for; each operation states the OAuth scope it requires. See the Authentication section. Operator-only routes require operator scopes and reject bank principals.

Security Scheme Type:

http

HTTP Authorization Scheme:

bearer

Bearer format:

JWT

Contact

M10 Engineering: ops@m10.io

License

MIT