Banking is the read-only service that collects DGMOS customers' bank movements. The API is called by the DGMOS backend, never directly from a browser or a mobile app. Every request is signed with a secret API key that belongs to one customer and only sees that customer's data.
This page is generated from openapi.yaml and asyncapi.yaml. The contract itself is checked against the API's registered routes in the test suite, so the endpoints here are exactly the ones the running service serves.
Authentication
The key pair (access_key + secret_key) is issued for the customer by the banking team. The secret is shown only once; keep it in DGMOS's secret store and never put it in a client or in logs.
Every request carries three headers:
Header
Value
X-Access-Key
The public part of the key
X-Timestamp
Unix seconds. Must be within 5 minutes of the server clock.
X-Signature
hex(HMAC-SHA256(secret_key, text))
The signed text has four lines joined by \n, with no trailing newline:
METHOD\nPATH\nTIMESTAMP\nBODY
METHOD in upper case: GET, POST…
PATH is the path and query exactly as sent: /v1/transactions?from=2025-11-01
BODY is the raw body as sent; empty for GET.
A write request that already succeeded and arrives again with the same signature gets 409 duplicate_request. Failed requests may be retried with the same signature.
Key restrictions are enforced by the server: allowed IPs, allowed routes, per-minute limit, expiry, revocation.
GET /v1/me returns who the key belongs to. It is the best first call to verify a setup: on 401 check the signature, on 403 check the key's IP and route restrictions.
This section walks the whole path, from connecting a customer's bank to movements showing up in DGMOS. Each step links to its endpoint and event; jump to the reference for the details.
The bank sends the file or banking pulls it; banking parses it and stores the movements. DGMOS reads data from the API and learns about changes from events right away.
When a sync fails, a balance does not reconcile or a file cannot be read
1. Connecting a bank
A bank shares a customer's movements only on the customer's written instruction. Track that process with the application workflow; the technical connection is set up with the steps below. A connection starts as DRAFT and receives files only when ACTIVE.
DRAFTTESTINGACTIVE
Side states: SUSPENDED (no new files are accepted), DISABLED.
If the host key changes, pulling stops with HOST_KEY_MISMATCH. Do not change the config before the bank confirms the new fingerprint through an official channel.
The scheduler or a manual sync opens a job, which downloads new files from the bank's server.
QUEUEDRUNNINGSUCCEEDED
Transient errors are retried with growing delays; a permanent one ends as DEAD and publishes banking.sync.failed.v1. Jobs: GET /v1/connections/{id}/sync-jobs.
Push SFTP_PUSH
The bank drops the file with the connection's own user (c<connection id>). The server is write-only: no reading, deleting or listing.
Source IP and SSH key must match the connection. Uploading under a temporary name (.part, .tmp) and renaming is supported; an interrupted transfer is discarded.
ca.pem: verify the server certificate with it; do not turn verification off
IP allow-list
The consumer's egress IP must be allowed on the banking side
Declare your queue with a name starting with your user name: dgmos.banking-events. You have no rights on other names.
Bind it to banking.events. The routing key is the event type: # for all, banking.transactions.* for movements only.
Acknowledge after handling the message (manual ack). If handling stops halfway, the message comes back.
Deduplicate on eventId: delivery is at least once.
An event carries ids only; read current data from the API (table below).
Important: events published before your queue is bound never reach you. Declare and bind the queue once, before the first bank connection; after that, events wait in the queue while the broker or your consumer is down.
Every bank differs: the methods and formats it supports and the forms and documents it asks for. Banking keeps this in its bank catalog and serves it through the API; the catalog is updated as banks answer.
What a bank supports
channels in GET /v1/banks/{id} has one entry per method and format: channel_type, format, intraday (intraday reports) and availability_status.
availability_status
Meaning
UNKNOWN
No information yet
REPORTED
Stated by the bank or a customer, not verified
UNDER_REVIEW
Being verified (sample files, tests)
VERIFIED
Verified with real files; evidence in evidence_url/evidence_note
UNSUPPORTED, DEPRECATED
No connection can be opened with this method
What a bank asks for
GET /v1/application-templates?institution_id=&channel_type= returns that bank's current template for the method: the form fields to fill (company details, accounts, formats, frequency…) and the documents (the bank's instruction form, signature circular…). An application is opened from this template and the server checks what is missing.
Column mappings of banks that send CSV or XLSX are in profiles: GET /v1/profiles.
What is exchanged with the bank
Given to the bank
Asked from the bank
Always
Company and technical contacts; banking's fixed IP
The customer's signed instruction; format and version; real sample files; file naming; end-of-day time and intraday frequency; whether intraday reports are incremental or full; character encoding
Bank sends
SFTP address, user name and server fingerprint (push field)
Its SSH public key and source IPs
We pull
Our SSH public key for the connection
Server address, user, directory and host key fingerprint
PGP
Our PGP public key for the connection
The bank's signing public key
Environment-specific values (SFTP address and fingerprint, IP, event address) come from the banking team.
Banks in Türkiye
The banks in the catalog and the statement service each describes on its own corporate pages. REPORTED: the bank's page states SFTP or a web service. UNKNOWN: the page mentions only SWIFT, e-mail or FTP, or no channel; ask the bank for SFTP. No bank is VERIFIED before real files have been tested.
The same movement can appear in several files: an intraday report and the end-of-day statement, a resent file, a backfill. Banking stores every movement once.
The same file content arriving twice (SHA-256) is not processed again; the response says duplicate: true.
For banks with reliable references the bank reference is the identity; otherwise a fingerprint of date, amount, direction and description plus the position within the file is used.
Two movements with the same date and amount are not merged. Both are stored; the second is flagged with duplicate_candidate_of.
A movement that arrived as PENDING in an intraday report is updated in place to BOOKED when the end-of-day statement books it.
occurrences tells in how many distinct statements the movement appeared.
Quarantine
When a statement does not match any account with certainty, nothing is guessed: the statement goes to quarantine, banking.statement.quarantined.v1 is published and no movements are written. The decision is left to the user on the DGMOS side.
POST /v1/quarantine/{id}/resolve imports the statement into the chosen account. If the account number in the file differs, force: true and a note are required. A currency mismatch can never be forced.
A bank integration application starts from a versioned template, the form and documents are completed, and it goes through operations and bank steps. Allowed transitions are in allowed_next of the getApplication response; the server checks its rules (missing documents, references, connection status) on every transition.
Side paths: CUSTOMER_ACTION_REQUIRED, RETURNED_FOR_CORRECTION, SUSPENDED; endings: REJECTED, CANCELLED, EXPIRED. Every transition publishes banking.application.state_changed.v1.
Events
Banking publishes changes to the banking.events topic exchange in RabbitMQ; the routing key is the event type. An event is queued in the same database transaction that stores the change, so it cannot get lost.
Delivery is at least once: the same event may arrive twice. Deduplicate on eventId.
Events carry ids only; read the current data from the API.
Fields are added in a backward-compatible way; a breaking change comes as a new .vN type.
New movements were added to an account. Sent per account and statement when at least one new movement was stored. Read them with GET /v1/accounts/{accountId}/transactions. aggregateType account.
Formats: MT940, MT942, CAMT.053, CAMT.052 (auto-detected), CSV and XLSX (need profile_id). Re-uploading the same file returns 200 with duplicate: true; movements already known are counted as seen, never duplicated. Statements that do not match the account are quarantined.
data, _ := os.ReadFile("statement_251104.sta")
var res IngestResult
err := c.ImportStatement(ctx, 42, "statement_251104.sta", data, "", 0, &res)
// empty format = detected from the content; pass profileID for CSV/XLSX