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_TRANSACTIONonFI0000000000000000withsettlesnaming the invoice. The invoice settles andorigin.settledfires. - Money out. Create a payment, then
POST …/sandbox/payments/{paymentId}/executeor…/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
settlementon 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
versionas 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.statusstaysPENDING_PROVIDER, and bills arrive only from you. - VAT return filing and balance-level reconciliation.