Integrating with Steinvar.
A short evaluation pack: how a caller authenticates, one request and its response, errors, idempotency, webhooks and versioning. It is taken from the platform’s API contract; the values in the examples are illustrative. The full API definitions are shared with technical access.
Authentication
Each portal has its own REST API: HQ for the institution’s staff, the branch back office, and customer e-banking. A caller sends the bearer token the platform issues at sign-in for that portal (a JWT signed with RS512), and every request carries a correlation id of up to 128 characters, which comes back as the trace id.
A system you run calls in through a service account with the same sign-in. There are no separate API keys or OAuth client credentials today. Each route is also checked against the caller’s role, its session and the modules the workplace rents.
Authorization: Bearer <token issued at sign-in>
X-Correlation-ID: 3f6c2a0e-5d1b-4a8e-9f6d-2b7e1c0a9d34
# Without the correlation header
401 { "errors": [{ "error": "JWT_INVALID",
"message": "missing correlation header" }],
"meta": { "trace_id": "…" } }A request and its response
Creating a folder in Docs, as its API definition specifies. Every success has the same envelope: the result under data, the trace id under meta.
Illustrative: the path, headers and fields are the contract’s; the ids, names and times are made up. Calling it needs a token for a workplace that rents Docs.
POST /api/v1/hq/apps/docflow/folders
Authorization: Bearer <token>
X-Correlation-ID: 3f6c2a0e-5d1b-4a8e-9f6d-2b7e1c0a9d34
Content-Type: application/json
{ "name": "Loan agreements" }
201 Created
{ "data": {
"id": "6720f1c4a9e3b25d1c8f0a17",
"parent_id": "",
"path": [],
"name": "Loan agreements",
"revision": 1,
"created_at": "2026-10-27T09:14:05Z",
"created_by": "6712ab90c3d4e5f60718293a",
"updated_at": "2026-10-27T09:14:05Z" },
"meta": { "trace_id": "3f6c2a0e-5d1b-4a8e-9f6d-2b7e1c0a9d34" } }Errors
Errors share one shape: a list of a machine-readable code in capitals and a message, with the trace id. Codes you meet early: JWT_MISSING and JWT_INVALID (401), FORBIDDEN (403), NOT_FOUND (404), FAILED_TO_DECODE_REQUEST (400), and VERSION_CONFLICT or INVALID_STATE_TRANSITION (409) when a record changed underneath you or cannot move to the state asked for.
409 Conflict
{ "errors": [
{ "error": "VERSION_CONFLICT",
"message": "the document changed; reload it and try again" } ],
"meta": { "trace_id": "3f6c2a0e-…" } }Illustrative message; the code and the shape are the contract’s.
Idempotency
Creating documents, every write in customer e-banking and the HQ routes that move money take an Idempotency-Key header of 8 to 255 characters. A key belongs to one caller, one method and one path. Send the same key when you retry:
- The same key and the same body, after the first request succeeded
- The stored response is returned again, with Idempotent-Replay: true. Nothing runs twice.
- The same key while the first request is still running
- 409 IDEMPOTENCY_IN_PROGRESS
- The same key with a different body
- 422 IDEMPOTENCY_KEY_REUSED
- The first request failed
- Nothing is stored; the same key can be used for the retry.
- No key where one is required, or a malformed key
- 400 IDEMPOTENCY_KEY_REQUIRED or IDEMPOTENCY_KEY_INVALID
Webhooks
Docs delivers its events to your HTTPS endpoint: documents created and signed, signatures completed, contracts expiring, and more. A payload carries ids and facts, never the document’s content. Each delivery is signed with HMAC-SHA256 over the timestamp, a dot and the raw body; reject a timestamp more than five minutes off, and skip an event id you have already handled.
- Retried on 408, 425, 429, 5xx and network errors, with exponential backoff (10 seconds doubling, at most an hour between attempts) and Retry-After honoured
- Given up after 12 attempts or 24 hours; any other 4xx marks the delivery failed
- A 410 pauses the subscription; 20 failures in a row disable it
- Receivers must be public HTTPS endpoints; the signing secret is shown once, when the subscription is created
POST https://your-system.example/steinvar/events
DocFlow-Signature: t=1792919645,v1=<hex HMAC-SHA256>
DocFlow-Event-Id: 6720f2d8a9e3b25d1c8f0a3c
DocFlow-Event-Type: document.signed
DocFlow-Delivery-Id: 6720f2d9a9e3b25d1c8f0a3d
{ "event_id": "6720f2d8a9e3b25d1c8f0a3c",
"event_type": "document.signed", "event_version": 1,
"source": "docflow", "timestamp": "2026-10-27T09:14:05Z",
"subject": { … }, "actor": { … }, "data": { … } }
# Node.js: is this delivery really from Steinvar?
const [t, ...sigs] = header.split(',').map(p => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret)
.update(t + '.' + rawBody).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(t)) <= 300;
const ok = fresh && sigs.includes(expected); Illustrative ids and times; the envelope also carries the workplace, correlation and trace ids. Two v1 values appear while a secret is being rotated. Use a constant-time comparison in production code.
Versioning
The API version is in the path: /api/v1/<portal>/apps/<module>, with hq, bo and eb as the portals. Each API definition carries its own version (Docs’ is 0.7.0, before a 1.0). Events carry theirs in the name a subscription lists, such as document.signed.v1.
Module dependencies
Which module needs Core, which needs another module, and what needs an outside provider: the packaging and dependency table.
Data and who integrates what
Webhooks are the only event feed for outside systems; the platform’s internal event streams are not exposed. Customers fetch their statements through e-banking in JSON, PDF or camt.053.
You bring
- The connection to your sponsor bank or PSP, scoped as integration work for each client
- Your own systems that call the APIs or receive webhooks, and the secrets they hold
- Commercial data providers you choose (ID verification, PEP and adverse media, credit bureau, qualified signatures)
- Checking that the configuration meets the rules that apply to you
The platform provides
- The REST APIs, the event delivery and its retries, and the definitions for the modules you evaluate
- Statements in JSON, PDF and camt.053, and CSV exports of the audit trail and loan reports for staff
- ISO 20022 messages generated and parsed for the payments you route to your partner
The standards and sources around a European institution.
Message formats, identity sources, watchlists and reference data are part of the platform. The payment rail itself is your sponsor bank or PSP.
Payment formats
- ISO 20022pacs.008, 002, 004; pain.001, 002; camt.052 to 056
- SEPA SCT and InstantEnvelopes for CENTROlink, ECB TIPS, SEPA-Clearer
- XAdESSigned payment files
- Your sponsor bank or PSPThe rail connection
Identity and company data
- EUDI WalletOpenID4VP, SD-JWT credentials
- ICAO 9303 ePassportsChip verification
- GLEIFLegal Entity Identifier lookup
- Lithuanian company registerCompany records and officers
Watchlists
- EU consolidated listRefreshed every six hours
- UN and OFACSDN and consolidated lists
- UK FCDOSanctions list
- Lithuanian FNTTNational list
Reference data
- Bank of Lithuania FX ratesDaily, with history and revaluation
- IBAN and BIC directoryValidation and bank registers
- Holiday calendarsBusiness days for deposits and night runs
Communications and billing
- E-mail and SMSSMTP and HTTP gateway providers
- SlackTeam alerts
- StripeModule billing and top-ups
Evidence and operations
- RFC 3161 timestampsOn sealed PDF documents
- Signed platform sealsAudit chain sealed every 15 minutes
- PrometheusMetrics from every service
- OpenTelemetryTraces across services, one trace id per request
- Health endpointsAlive and ready checks on every service
Need the full definitions?
Tell us your stack and the modules you are evaluating; we share the API definitions they need.