Developer essentials

Testing

Everything you build against Vexless can be exercised end to end without a real bank, a real supplier or a real accountant.

Sandbox companies

Create one per test run with "sandbox": true. It has the full chart, tax codes and numbering, a simulated bank account, and every event a live company has. Nothing is deleted in this version, so a fresh company per run beats resetting one; put the run id in your idempotency keys and external references. See Sandbox.

Dry runs

Send any origin with ?dryRun=true. The whole pipeline runs and is rolled back: the response is the origin as it would have been posted, with its entry, its allocations and the interpretations it would have recorded. Refusals come back exactly as they would for real.

POST /v1/companies/{companyId}/origins?dryRun=true
{ "type": "SALES_INVOICE", … }
{ "dryRun": true, "id": "org_…", "status": "POSTED", "invoiceNumber": 118, "entries": [ { "number": 218, "postings": [ … ] } ], "interpretations": [ … ], … }

Use it to validate what your software is about to send, to preview the postings before a user confirms, and in tests that check a request shape without leaving anything behind. Nothing is stored, no event fires, no number is consumed, and no idempotency key is needed or recorded.

Play the bank

  • Money in. Send a BANK_TRANSACTION on FI0000000000000000 with settles naming the invoice. The invoice settles and origin.settled fires.
  • Money out. Create a payment, then POST …/sandbox/payments/{paymentId}/execute or …/reject. Every state and event of a real payment follows.
  • Partial and over-payments. Send a transaction for less or more than the invoice and read settlement on the origin.
  • Duplicates. Send the same transaction twice and expect 409 ORIGIN_DUPLICATE; send it under a second id and reverse one.

Play the accountant

Grant your own partner review:write, accounts:manage and close:manage on the test company and you can exercise the paths your software only observes: interpret an origin you sent without postings, correct one, reject one, move the lock, close the year. Each fires the event your handler needs to cope with. See Grants.

POST /v1/companies/{companyId}/grants
{ "granteeKind": "VERTICAL", "granteeId": "ptn_yourself…", "scopes": ["review:write", "accounts:manage", "close:manage"] }

Webhook handlers

  • Point a webhook at a tunnel to your machine and run the quickstart; that produces most event types.
  • Answer slowly or with a 500 once and watch the retry arrive; check that your handler ignores the duplicate by event id.
  • Correct an origin as the accountant and check that your handler takes the higher version as current.
  • Verify the signature against a saved delivery in a unit test; the algorithm is on the Webhooks page.

Retries

Replay any POST with the same idempotency key and the same body and expect the stored response, status code included. Change the body and expect 409 IDEMPOTENCY_CONFLICT. See Idempotency.

Inspect what happened

GET /v1/companies/{companyId}/origins/{originId}?explain=true     # entries, settlement, payments, every interpretation and its actor
GET /v1/companies/{companyId}/open-items                          # what is still open, and why
GET /v1/companies/{companyId}/balances?asOf=                      # the books at a date

Assert on these rather than on event order. Events are at least once and unordered; the API is the truth.

What you cannot test yet

  • A real bank connection: the consent flow ends at NEEDS_AUTH.
  • E-invoice delivery and receipt: delivery.status stays PENDING_PROVIDER, and bills arrive only from you.
  • VAT return filing and balance-level reconciliation.