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.

Request headers
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 a folder in 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.

A conflict on a stale revision
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
A delivery, and checking it
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.

Request technical access