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 asexternal_account_id(ISO 20022Max34Text: 1 to 34 characters), anasset_id, and an account type (CUSTOMERorTREASURY). Internal holding and issuance accounts are managed by the platform and are not exposed. - Transfer assets with
POST /v1/transfers, choosing atransfer_kind(INTERBANKto a known account at another bank,BANK_ROUTEDto another bank identified by bank and currency; see the Transfers section for the kinds currently available). - Approve work through the
reviewsresource when policy requires human sign-off (currently every transfer waits inPENDING_REVIEWfor 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/exposuresand, 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/messagesas 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:
POSTandPUTrequests must sendContent-Type: application/vnd.api+jsonexactly, without parameters such ascharset; otherwise the response is415 Unsupported Media Type.- If you send an
Acceptheader, it must listapplication/vnd.api+json; a header such asAccept: */*(the default of many HTTP clients, including curl) gets406 Not Acceptable. Either sendAccept: application/vnd.api+jsonor omit the header. - A request body is a document with a top-level
datamember holdingtypeandattributes. A single resource is returned asdata: \{type, id, attributes, relationships, links\}; a collection asdata: [...]plus paginationlinks. Related resources are linked throughrelationships.<name>.links.related. - The
include(compound documents) andfields[<type>](sparse fieldsets) parameters are accepted but not applied yet: responses never containincludedand 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 state | Meaning |
|---|---|
SUCCEEDED | The request took effect. |
REJECTED | The request was refused; see result_code and result_message. |
FAILED | Execution failed; see result_code and result_message. |
CANCELLED | The request was cancelled before it took effect. |
EXPIRED | A 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 Accepteddocument 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 Conflictwith codeIDEMPOTENCY_CONFLICT. - Same key while the first request is still being accepted:
409 Conflictwith codeIDEMPOTENCY_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
4xxor a5xx), the key stays reserved; the same key and body getIDEMPOTENCY_PENDINGfor 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 gives400 Bad Request.- The first page is returned when
page[cursor]is absent. Each response haslinks.selfand, when more results exist,links.next, which repeats your query with the next cursor. Followlinks.nextuntil it is absent. - Cursors are opaque; do not build or parse them. Only forward paging is supported
(
links.previs 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" }
}
]
}
| Status | When | Retry? |
|---|---|---|
400 | Malformed JSON, invalid parameter or field, missing Idempotency-Key | No; fix the request |
401 | Missing, malformed, expired or foreign token (AUTHENTICATION_REQUIRED) | After getting a new token |
403 | Missing scope or bank membership, wrong x-dan-bank-id, suspended or terminated bank | No |
404 | Resource does not exist or is not visible to your bank | No |
406 | Accept header does not list application/vnd.api+json | No; fix the header |
409 | Idempotency conflict or pending request, or a state conflict | Depends on code |
415 | Content-Type is not exactly application/vnd.api+json | No; fix the header |
422 | Body does not match the expected shape, or breaks a business rule | No; fix the request |
500, 502 | Unexpected platform error | Yes, with the same Idempotency-Key and body (it may answer IDEMPOTENCY_PENDING for up to 5 minutes first) |
503 | Network halted or read-only (OUTBOUND_HALTED, READ_ONLY), state unknown, or a dependency unavailable | Yes, 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
- HTTP: Bearer Auth
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