WOMMWorks On My Machine · onmymachineitworks.comSample · fictional data

Smoke test report · API

Acme Ledger Public API · Staging

Passed with issues
Environment
https://api.staging.acme-ledger.example
API version
2.4.0 (OpenAPI 3.1)
Build
4.2.0-rc1
Executed
2026-10-20 09:12 – 09:21 UTC
Executed by
Tomasz Kordiak (QA lead), suite in acme/qa-api-tests
Scope
Read-only. Writes use dryRun=true; no data created, modified or deleted.

Overview

98.9%pass rate
270Tests
265Passed
3Failed
2Skipped
249 msMedian response
64Operations tested
Auth
14/14
Customers
23/23
Contacts
16/16
Products
19/19
Invoices
32/35 · 2 failed · 1 skipped
Payments
21/22 · 1 skipped
Credit notes
17/17
Recurring schedules
19/19
Taxes
14/14
Webhooks
19/20 · 1 failed
Reports
19/19
Users and roles
20/20
Files
16/16
Audit log
16/16

Response time: median 249 ms, p95 470 ms, max 855 ms. Pass rate excludes skipped tests. Every successful response was validated against the OpenAPI schema.

Environment and scope

Base URLhttps://api.staging.acme-ledger.example/v2
Test tenantst-demo-01 (1,019 invoices, 7-year retention), t-demo-02 (265 invoices, 365-day retention)
Clients3 OAuth clients: full scope, read-only scope, no scope
Users5 roles: Admin, Finance, Accountant, Sales, Viewer
ToolingPython 3.12, pytest, schemathesis for schema checks; runs in CI on every deploy to staging
Out of scopeWrite operations without dryRun, payment provider callbacks, load (see performance report)
Entry criteriaBuild deployed, health check green, seed data loaded — met at 09:05 UTC

Issues found 3 failed tests

High INV-26 Invoices past data retention are listed but cannot be retrieved

Observed
GET /invoices returns invoices whose retention period has expired. GET /invoices/{id} returns 404 Invoice not found for the same IDs.
Evidence
5 of the first 20 listed invoices; 41 of 1,284 in total. All belong to t-demo-02, created before 2025-10-20, with retention.days = 365. Reproduced 3 times per ID.
Impact
Integrations that list then fetch get unexpected 404s. totalItems and dashboards include effectively deleted records.
Expected
Expired invoices excluded from the list and from totalItems.
Defect
ACME-512 · full detail in the test case execution report ACME-T-118

Medium INV-25 Invalid status filter value is silently ignored

Observed
GET /invoices?status=payed returns 200 with all 1,284 invoices. The spec documents 400.
Evidence
Control checks: status=paid → 913, status=open → 371.
Impact
A typo returns unfiltered data instead of an error.
Defect
ACME-513

Medium WH-19 Webhook retries ignore the configured back-off

Observed
Subscription configured with back-off 1 min / 5 min / 15 min; the delivery log shows the 3rd retry 1 minute after the 2nd.
Impact
Receivers that are down get retried too fast, and the event is marked failed 13 minutes early.
Defect
ACME-514

Defects 4 reported

KeySummarySeverityStatusComponentFound byOwnerCreated
ACME-512Invoice list returns invoices already removed by data retention (404 on detail)HighOpenInvoices APIINV-26Backend20 Oct 2026
ACME-513Invalid status filter value silently ignored instead of 400MediumOpenInvoices APIINV-25Backend20 Oct 2026
ACME-514Webhook retry ignores the configured back-off on the 3rd attemptMediumOpenWebhooksWH-19Platform20 Oct 2026
ACME-516OpenAPI spec documents limit max 200, API enforces 100
Found while writing checks; the API behaviour is correct, the spec is not.
LowOpenAPI docs–Backend20 Oct 2026

Observations no defect, worth knowing

IDAreaObservationImpactSuggested action
OBS-01CustomersGET /customers/{id} for another tenant returns 404, not 403.NoneNone — consistent with docs and hides which IDs exist.
OBS-02InvoicesInvoice PDF is the slowest operation (p95 ≈ 1 s).PerformanceInclude in the load test (done: see performance report).
OBS-03AuthHTTP requests are redirected with 301; clients that don't follow redirects fail.LowDocs already say HTTPS only; no change.
OBS-04PaginationCursor values are base64 JSON and decodable by clients.LowFine if clients are told cursors are opaque; add a line to the docs.
OBS-05ErrorsValidation errors return the first failing field only.UsabilityConsider returning all field errors at once (API v3).
OBS-06Rate limitingLimit is 100 requests/minute per client; bursts are not smoothed.LowDocument the limit per endpoint group.
OBS-07WebhooksWebhook URLs resolving to private IPs are rejected (SSRF protection).PositiveKeep; covered by WH validation check.
OBS-08Skipped2 checks skipped: Peppol export (flag off on staging) and SEPA mandate (sandbox down).CoverageRun both on rc3 once available.

Coverage of the API

AreaDocumented operationsTestedNot tested
Auth and platform44–
Customers and contacts99–
Products and taxes77–
Invoices1210POST /invoices/{id}/duplicate (deprecated), PUT /invoices/{id}/lines (beta, not released)
Payments and credit notes109POST /mandates (skipped, sandbox down)
Recurring schedules55–
Webhooks66–
Reports55–
Users, roles, files, audit log119POST /users/{id}/impersonate (admin tool, excluded), PATCH /users/{id}/locale (UI only)

69 documented operations, 64 tested (93%). Not tested operations are listed with the reason.

Response times by area

AreaTestsMedianp95Max
Auth14243 ms349 ms349 ms
Customers23225 ms331 ms348 ms
Contacts16258 ms347 ms347 ms
Products19222 ms345 ms345 ms
Invoices34255 ms770 ms823 ms
Payments21235 ms324 ms341 ms
Credit notes17251 ms855 ms855 ms
Recurring schedules19249 ms338 ms338 ms
Taxes14231 ms337 ms337 ms
Webhooks20230 ms336 ms336 ms
Reports19488 ms594 ms594 ms
Users and roles20227 ms333 ms333 ms
Files16243 ms349 ms349 ms
Audit log16259 ms348 ms348 ms

Test results 270 tests

Auth 14/14 passed

IDTestEndpointExpectedActualTimeResult
AUTH-01Token issued for valid client credentialsPOST /oauth/token200200120 msPass
AUTH-02Invalid client secret rejectedPOST /oauth/token401401173 msPass
AUTH-03Unknown client id rejectedPOST /oauth/token401401226 msPass
AUTH-04Request without token rejectedGET /invoices401401279 msPass
AUTH-05Expired token rejectedGET /invoices401401332 msPass
AUTH-06Token signed with wrong key rejectedGET /invoices401401155 msPass
AUTH-07Token for tenant A cannot read tenant BGET /invoices/{id}404404208 msPass
AUTH-08Scopes listed in the token are enforcedGET /reports/revenue403403261 msPass
AUTH-09Token lifetime is 3600 sPOST /oauth/tokenexpires_in 3600expires_in 3600314 msPass
AUTH-10HTTP request redirected to HTTPSGET http://…/invoices301301137 msPass
AUTH-11CORS: unknown origin not allowedOPTIONS /invoicesno ACAO headerno ACAO header190 msPass
AUTH-12Rate limit headers presentGET /invoicesX-RateLimit-*X-RateLimit-*243 msPass
AUTH-13Rate limit returns 429 with Retry-AfterGET /invoices × 120/min429429296 msPass
AUTH-14Health endpoint without authGET /health200200349 msPass

Customers 23/23 passed

IDTestEndpointExpectedActualTimeResult
CUS-01List customersGET /customers200200172 msPass
CUS-02Pagination: page size and next cursorGET /customers?limit=2020 + cursor20 + cursor225 msPass
CUS-03Pagination: last page has no cursorGET /customers?cursor=…no cursorno cursor278 msPass
CUS-04Limit above 100 rejectedGET /customers?limit=500400400331 msPass
CUS-05Sort by creation date, descendingGET /customers?sort=-createdAtsortedsorted154 msPass
CUS-06Filter by nameGET /customers?name=…matching items onlymatching items only207 msPass
CUS-07Filter by vatNumberGET /customers?vatNumber=…matching items onlymatching items only260 msPass
CUS-08Filter by countryGET /customers?country=…matching items onlymatching items only313 msPass
CUS-09Filter by createdFromGET /customers?createdFrom=…matching items onlymatching items only136 msPass
CUS-10Filter by tagGET /customers?tag=…matching items onlymatching items only189 msPass
CUS-11Get by idGET /customers/{id}200200242 msPass
CUS-12Unknown idGET /customers/{id}404404295 msPass
CUS-13Other tenant's idGET /customers/{id}404404348 msPass
CUS-14Response matches OpenAPI schemaGET /customers/{id}validvalid171 msPass
CUS-15Create (dry run)POST /customers?dryRun=true200200224 msPass
CUS-16Rejects missing namePOST /customers?dryRun=true400400277 msPass
CUS-17Rejects invalid emailPOST /customers?dryRun=true400400330 msPass
CUS-18Rejects invalid country codePOST /customers?dryRun=true400400153 msPass
CUS-19Rejects VAT number formatPOST /customers?dryRun=true400400206 msPass
CUS-20Rejects name over 200 charsPOST /customers?dryRun=true400400259 msPass
CUS-21Missing scope rejectedGET /customers403403312 msPass
CUS-22Customer with open invoices cannot be deletedDELETE /customers/{id}?dryRun=true409409135 msPass
CUS-23Archived customers excluded by defaultGET /customersarchived hiddenarchived hidden188 msPass

Contacts 16/16 passed

IDTestEndpointExpectedActualTimeResult
CON-01List contactsGET /customers/{id}/contacts200200241 msPass
CON-02Pagination: page size and next cursorGET /customers/{id}/contacts?limit=2020 + cursor20 + cursor294 msPass
CON-03Pagination: last page has no cursorGET /customers/{id}/contacts?cursor=…no cursorno cursor347 msPass
CON-04Limit above 100 rejectedGET /customers/{id}/contacts?limit=500400400170 msPass
CON-05Sort by creation date, descendingGET /customers/{id}/contacts?sort=-createdAtsortedsorted223 msPass
CON-06Filter by roleGET /customers/{id}/contacts?role=…matching items onlymatching items only276 msPass
CON-07Filter by emailGET /customers/{id}/contacts?email=…matching items onlymatching items only329 msPass
CON-08Get by idGET /customers/{id}/contacts/{cid}200200152 msPass
CON-09Unknown idGET /customers/{id}/contacts/{cid}404404205 msPass
CON-10Other tenant's idGET /customers/{id}/contacts/{cid}404404258 msPass
CON-11Response matches OpenAPI schemaGET /customers/{id}/contacts/{cid}validvalid311 msPass
CON-12Create (dry run)POST /customers/{id}/contacts?dryRun=true200200134 msPass
CON-13Rejects missing emailPOST /customers/{id}/contacts?dryRun=true400400187 msPass
CON-14Rejects duplicate primary contactPOST /customers/{id}/contacts?dryRun=true400400240 msPass
CON-15Missing scope rejectedGET /customers/{id}/contacts403403293 msPass
CON-16Primary contact is unique per customerGET /customers/{id}/contacts1 primary1 primary346 msPass

Products 19/19 passed

IDTestEndpointExpectedActualTimeResult
PRD-01List productsGET /products200200169 msPass
PRD-02Pagination: page size and next cursorGET /products?limit=2020 + cursor20 + cursor222 msPass
PRD-03Pagination: last page has no cursorGET /products?cursor=…no cursorno cursor275 msPass
PRD-04Limit above 100 rejectedGET /products?limit=500400400328 msPass
PRD-05Sort by creation date, descendingGET /products?sort=-createdAtsortedsorted151 msPass
PRD-06Filter by skuGET /products?sku=…matching items onlymatching items only204 msPass
PRD-07Filter by activeGET /products?active=…matching items onlymatching items only257 msPass
PRD-08Filter by vatRateGET /products?vatRate=…matching items onlymatching items only310 msPass
PRD-09Get by idGET /products/{id}200200133 msPass
PRD-10Unknown idGET /products/{id}404404186 msPass
PRD-11Other tenant's idGET /products/{id}404404239 msPass
PRD-12Response matches OpenAPI schemaGET /products/{id}validvalid292 msPass
PRD-13Create (dry run)POST /products?dryRun=true200200345 msPass
PRD-14Rejects negative unit pricePOST /products?dryRun=true400400168 msPass
PRD-15Rejects unknown VAT ratePOST /products?dryRun=true400400221 msPass
PRD-16Rejects missing namePOST /products?dryRun=true400400274 msPass
PRD-17Rejects SKU already usedPOST /products?dryRun=true400400327 msPass
PRD-18Missing scope rejectedGET /products403403150 msPass
PRD-19Price history kept after price changeGET /products/{id}/prices2 entries2 entries203 msPass

Invoices 32/35 passed

IDTestEndpointExpectedActualTimeResult
INV-01List invoicesGET /invoices200200256 msPass
INV-02Pagination: page size and next cursorGET /invoices?limit=2020 + cursor20 + cursor309 msPass
INV-03Pagination: last page has no cursorGET /invoices?cursor=…no cursorno cursor132 msPass
INV-04Limit above 100 rejectedGET /invoices?limit=500400400185 msPass
INV-05Sort by creation date, descendingGET /invoices?sort=-createdAtsortedsorted238 msPass
INV-06Filter by statusGET /invoices?status=…matching items onlymatching items only291 msPass
INV-07Filter by customerIdGET /invoices?customerId=…matching items onlymatching items only344 msPass
INV-08Filter by issuedFromGET /invoices?issuedFrom=…matching items onlymatching items only167 msPass
INV-09Filter by issuedToGET /invoices?issuedTo=…matching items onlymatching items only220 msPass
INV-10Filter by dueFromGET /invoices?dueFrom=…matching items onlymatching items only273 msPass
INV-11Filter by currencyGET /invoices?currency=…matching items onlymatching items only326 msPass
INV-12Filter by numberGET /invoices?number=…matching items onlymatching items only149 msPass
INV-13Get by idGET /invoices/{id}200200202 msPass
INV-14Unknown idGET /invoices/{id}404404255 msPass
INV-15Other tenant's idGET /invoices/{id}404404308 msPass
INV-16Response matches OpenAPI schemaGET /invoices/{id}validvalid131 msPass
INV-17Create (dry run)POST /invoices?dryRun=true200200184 msPass
INV-18Rejects negative quantityPOST /invoices?dryRun=true400400237 msPass
INV-19Rejects unknown currencyPOST /invoices?dryRun=true400400290 msPass
INV-20Rejects missing customerPOST /invoices?dryRun=true400400343 msPass
INV-21Rejects due date before issue datePOST /invoices?dryRun=true400400166 msPass
INV-22Rejects more than 500 linesPOST /invoices?dryRun=true400400219 msPass
INV-23Rejects discount over 100%POST /invoices?dryRun=true400400272 msPass
INV-24Missing scope rejectedGET /invoices403403325 msPass
INV-25Invalid status value rejected · ACME-513GET /invoices?status=payed400200148 msFail
INV-26Every listed invoice can be retrieved · ACME-512GET /invoices → /invoices/{id}200404 for 5 of 20201 msFail
INV-27Totals computed correctly (net, VAT, gross)POST /invoices?dryRun=truematchmatch254 msPass
INV-28Rounding: 3 × €0.333 at 23% VATPOST /invoices?dryRun=true€1.23 gross€1.23 gross307 msPass
INV-29Invoice PDF downloadGET /invoices/{id}/pdf200 application/pdf200 application/pdf770 msPass
INV-30PDF of another tenant's invoiceGET /invoices/{id}/pdf404404823 msPass
INV-31totalItems equals the sum of pagesGET /invoicesequalequal236 msPass
INV-32Numbering is sequential per seriesGET /invoices?series=2026no gapsno gaps289 msPass
INV-33Send invoice (dry run)POST /invoices/{id}/send?dryRun=true202202342 msPass
INV-34Cancelled invoice cannot be sentPOST /invoices/{id}/send?dryRun=true409409165 msPass
INV-35Peppol e-invoice export (feature flag off on staging)GET /invoices/{id}/ubl200––Skipped

Payments 21/22 passed

IDTestEndpointExpectedActualTimeResult
PAY-01List paymentsGET /payments200200271 msPass
PAY-02Pagination: page size and next cursorGET /payments?limit=2020 + cursor20 + cursor324 msPass
PAY-03Pagination: last page has no cursorGET /payments?cursor=…no cursorno cursor147 msPass
PAY-04Limit above 100 rejectedGET /payments?limit=500400400200 msPass
PAY-05Sort by creation date, descendingGET /payments?sort=-createdAtsortedsorted253 msPass
PAY-06Filter by invoiceIdGET /payments?invoiceId=…matching items onlymatching items only306 msPass
PAY-07Filter by methodGET /payments?method=…matching items onlymatching items only129 msPass
PAY-08Filter by receivedFromGET /payments?receivedFrom=…matching items onlymatching items only182 msPass
PAY-09Get by idGET /payments/{id}200200235 msPass
PAY-10Unknown idGET /payments/{id}404404288 msPass
PAY-11Other tenant's idGET /payments/{id}404404341 msPass
PAY-12Response matches OpenAPI schemaGET /payments/{id}validvalid164 msPass
PAY-13Create (dry run)POST /payments?dryRun=true200200217 msPass
PAY-14Rejects overpaymentPOST /payments?dryRun=true400400270 msPass
PAY-15Rejects payment on cancelled invoicePOST /payments?dryRun=true400400323 msPass
PAY-16Rejects amount with 3 decimalsPOST /payments?dryRun=true400400146 msPass
PAY-17Rejects currency differs from invoicePOST /payments?dryRun=true400400199 msPass
PAY-18Missing scope rejectedGET /payments403403252 msPass
PAY-19Partial payment updates the open balancePOST /payments?dryRun=truebalance reducedbalance reduced305 msPass
PAY-20Refund (dry run)POST /payments/{id}/refund?dryRun=true200200128 msPass
PAY-21Refund above paid amount rejectedPOST /payments/{id}/refund?dryRun=true422422181 msPass
PAY-22SEPA direct debit mandate (provider sandbox unavailable)POST /mandates?dryRun=true201––Skipped

Credit notes 17/17 passed

IDTestEndpointExpectedActualTimeResult
CN-01List credit notesGET /credit-notes200200287 msPass
CN-02Pagination: page size and next cursorGET /credit-notes?limit=2020 + cursor20 + cursor340 msPass
CN-03Pagination: last page has no cursorGET /credit-notes?cursor=…no cursorno cursor163 msPass
CN-04Limit above 100 rejectedGET /credit-notes?limit=500400400216 msPass
CN-05Sort by creation date, descendingGET /credit-notes?sort=-createdAtsortedsorted269 msPass
CN-06Filter by invoiceIdGET /credit-notes?invoiceId=…matching items onlymatching items only322 msPass
CN-07Filter by issuedFromGET /credit-notes?issuedFrom=…matching items onlymatching items only145 msPass
CN-08Get by idGET /credit-notes/{id}200200198 msPass
CN-09Unknown idGET /credit-notes/{id}404404251 msPass
CN-10Other tenant's idGET /credit-notes/{id}404404304 msPass
CN-11Response matches OpenAPI schemaGET /credit-notes/{id}validvalid127 msPass
CN-12Create (dry run)POST /credit-notes?dryRun=true200200180 msPass
CN-13Rejects credit above invoice totalPOST /credit-notes?dryRun=true400400233 msPass
CN-14Rejects credit on draft invoicePOST /credit-notes?dryRun=true400400286 msPass
CN-15Missing scope rejectedGET /credit-notes403403339 msPass
CN-16Credit note links to its invoiceGET /credit-notes/{id}invoiceId setinvoiceId set162 msPass
CN-17Credit note PDF downloadGET /credit-notes/{id}/pdf200200855 msPass

Recurring schedules 19/19 passed

IDTestEndpointExpectedActualTimeResult
REC-01List recurring schedulesGET /recurring200200268 msPass
REC-02Pagination: page size and next cursorGET /recurring?limit=2020 + cursor20 + cursor321 msPass
REC-03Pagination: last page has no cursorGET /recurring?cursor=…no cursorno cursor144 msPass
REC-04Limit above 100 rejectedGET /recurring?limit=500400400197 msPass
REC-05Sort by creation date, descendingGET /recurring?sort=-createdAtsortedsorted250 msPass
REC-06Filter by customerIdGET /recurring?customerId=…matching items onlymatching items only303 msPass
REC-07Filter by frequencyGET /recurring?frequency=…matching items onlymatching items only126 msPass
REC-08Filter by activeGET /recurring?active=…matching items onlymatching items only179 msPass
REC-09Get by idGET /recurring/{id}200200232 msPass
REC-10Unknown idGET /recurring/{id}404404285 msPass
REC-11Other tenant's idGET /recurring/{id}404404338 msPass
REC-12Response matches OpenAPI schemaGET /recurring/{id}validvalid161 msPass
REC-13Create (dry run)POST /recurring?dryRun=true200200214 msPass
REC-14Rejects day of month 0POST /recurring?dryRun=true400400267 msPass
REC-15Rejects end date before startPOST /recurring?dryRun=true400400320 msPass
REC-16Rejects unknown frequencyPOST /recurring?dryRun=true400400143 msPass
REC-17Missing scope rejectedGET /recurring403403196 msPass
REC-18Next 3 run dates returnedGET /recurring/{id}3 dates3 dates249 msPass
REC-19Day 31 falls back to last day of monthGET /recurring/{id}30 Nov30 Nov302 msPass

Taxes 14/14 passed

IDTestEndpointExpectedActualTimeResult
TAX-01List taxesGET /tax-rates200200125 msPass
TAX-02Pagination: page size and next cursorGET /tax-rates?limit=2020 + cursor20 + cursor178 msPass
TAX-03Pagination: last page has no cursorGET /tax-rates?cursor=…no cursorno cursor231 msPass
TAX-04Limit above 100 rejectedGET /tax-rates?limit=500400400284 msPass
TAX-05Sort by creation date, descendingGET /tax-rates?sort=-createdAtsortedsorted337 msPass
TAX-06Filter by countryGET /tax-rates?country=…matching items onlymatching items only160 msPass
TAX-07Get by idGET /tax-rates/{id}200200213 msPass
TAX-08Unknown idGET /tax-rates/{id}404404266 msPass
TAX-09Other tenant's idGET /tax-rates/{id}404404319 msPass
TAX-10Response matches OpenAPI schemaGET /tax-rates/{id}validvalid142 msPass
TAX-11Create (dry run)POST /tax-rates?dryRun=true200200195 msPass
TAX-12Rejects rate over 100%POST /tax-rates?dryRun=true400400248 msPass
TAX-13Missing scope rejectedGET /tax-rates403403301 msPass
TAX-14Reverse charge flag on EU B2B customerPOST /invoices?dryRun=trueVAT 0%, note addedVAT 0%, note added124 msPass

Webhooks 19/20 passed

IDTestEndpointExpectedActualTimeResult
WH-01List webhooksGET /webhooks200200177 msPass
WH-02Pagination: page size and next cursorGET /webhooks?limit=2020 + cursor20 + cursor230 msPass
WH-03Pagination: last page has no cursorGET /webhooks?cursor=…no cursorno cursor283 msPass
WH-04Limit above 100 rejectedGET /webhooks?limit=500400400336 msPass
WH-05Sort by creation date, descendingGET /webhooks?sort=-createdAtsortedsorted159 msPass
WH-06Filter by eventGET /webhooks?event=…matching items onlymatching items only212 msPass
WH-07Get by idGET /webhooks/{id}200200265 msPass
WH-08Unknown idGET /webhooks/{id}404404318 msPass
WH-09Other tenant's idGET /webhooks/{id}404404141 msPass
WH-10Response matches OpenAPI schemaGET /webhooks/{id}validvalid194 msPass
WH-11Create (dry run)POST /webhooks?dryRun=true200200247 msPass
WH-12Rejects HTTP (non-TLS) URLPOST /webhooks?dryRun=true400400300 msPass
WH-13Rejects unknown event typePOST /webhooks?dryRun=true400400123 msPass
WH-14Rejects URL resolving to private IPPOST /webhooks?dryRun=true400400176 msPass
WH-15Missing scope rejectedGET /webhooks403403229 msPass
WH-16Signature header on test deliveryPOST /webhooks/{id}/testX-Acme-SignatureX-Acme-Signature282 msPass
WH-17Signature verifies with the shared secretPOST /webhooks/{id}/testvalidvalid335 msPass
WH-18Delivery log lists the test eventGET /webhooks/{id}/deliveries200200158 msPass
WH-19Retry uses configured back-off (3rd attempt) · ACME-514GET /webhooks/{id}/deliveriesafter 15 minafter 1 min211 msFail
WH-20Secret rotation keeps old secret valid for 24 hPOST /webhooks/{id}/rotate?dryRun=trueboth validboth valid264 msPass

Reports 19/19 passed

IDTestEndpointExpectedActualTimeResult
REP-01List reportsGET /reports/revenue200200577 msPass
REP-02Pagination: page size and next cursorGET /reports/revenue?limit=2020 + cursor20 + cursor400 msPass
REP-03Pagination: last page has no cursorGET /reports/revenue?cursor=…no cursorno cursor453 msPass
REP-04Limit above 100 rejectedGET /reports/revenue?limit=500400400506 msPass
REP-05Sort by creation date, descendingGET /reports/revenue?sort=-createdAtsortedsorted559 msPass
REP-06Filter by monthGET /reports/revenue?month=…matching items onlymatching items only382 msPass
REP-07Filter by currencyGET /reports/revenue?currency=…matching items onlymatching items only435 msPass
REP-08Get by idGET /reports/revenue/{id}200200488 msPass
REP-09Unknown idGET /reports/revenue/{id}404404541 msPass
REP-10Other tenant's idGET /reports/revenue/{id}404404594 msPass
REP-11Response matches OpenAPI schemaGET /reports/revenue/{id}validvalid417 msPass
REP-12Create (dry run)POST /reports/revenue?dryRun=true200200470 msPass
REP-13Rejects month in wrong formatPOST /reports/revenue?dryRun=true400400523 msPass
REP-14Missing scope rejectedGET /reports/revenue403403576 msPass
REP-15Aged receivables reportGET /reports/aged-receivables200200399 msPass
REP-16CSV exportGET /reports/revenue.csvtext/csvtext/csv452 msPass
REP-17Report totals equal invoice totalsGET /reports/revenueequalequal505 msPass
REP-18Future month returns empty reportGET /reports/revenue?month=2027-01200, 0 rows200, 0 rows558 msPass
REP-19VAT summary per rateGET /reports/vatrates sum to totalrates sum to total381 msPass

Users and roles 20/20 passed

IDTestEndpointExpectedActualTimeResult
USR-01List users and rolesGET /users200200174 msPass
USR-02Pagination: page size and next cursorGET /users?limit=2020 + cursor20 + cursor227 msPass
USR-03Pagination: last page has no cursorGET /users?cursor=…no cursorno cursor280 msPass
USR-04Limit above 100 rejectedGET /users?limit=500400400333 msPass
USR-05Sort by creation date, descendingGET /users?sort=-createdAtsortedsorted156 msPass
USR-06Filter by roleGET /users?role=…matching items onlymatching items only209 msPass
USR-07Filter by activeGET /users?active=…matching items onlymatching items only262 msPass
USR-08Get by idGET /users/{id}200200315 msPass
USR-09Unknown idGET /users/{id}404404138 msPass
USR-10Other tenant's idGET /users/{id}404404191 msPass
USR-11Response matches OpenAPI schemaGET /users/{id}validvalid244 msPass
USR-12Create (dry run)POST /users?dryRun=true200200297 msPass
USR-13Rejects invite with invalid emailPOST /users?dryRun=true400400120 msPass
USR-14Rejects unknown rolePOST /users?dryRun=true400400173 msPass
USR-15Missing scope rejectedGET /users403403226 msPass
USR-16Current userGET /users/me200200279 msPass
USR-17Roles are listedGET /roles5 roles5 roles332 msPass
USR-18Accountant cannot invite usersPOST /users/invite?dryRun=true403403155 msPass
USR-19Viewer cannot create invoicesPOST /invoices?dryRun=true403403208 msPass
USR-20Admin cannot remove the last adminDELETE /users/{id}?dryRun=true409409261 msPass

Files 16/16 passed

IDTestEndpointExpectedActualTimeResult
FIL-01List filesGET /files200200314 msPass
FIL-02Pagination: page size and next cursorGET /files?limit=2020 + cursor20 + cursor137 msPass
FIL-03Pagination: last page has no cursorGET /files?cursor=…no cursorno cursor190 msPass
FIL-04Limit above 100 rejectedGET /files?limit=500400400243 msPass
FIL-05Sort by creation date, descendingGET /files?sort=-createdAtsortedsorted296 msPass
FIL-06Filter by typeGET /files?type=…matching items onlymatching items only349 msPass
FIL-07Get by idGET /files/{id}200200172 msPass
FIL-08Unknown idGET /files/{id}404404225 msPass
FIL-09Other tenant's idGET /files/{id}404404278 msPass
FIL-10Response matches OpenAPI schemaGET /files/{id}validvalid331 msPass
FIL-11Create (dry run)POST /files?dryRun=true200200154 msPass
FIL-12Rejects file type not allowedPOST /files?dryRun=true400400207 msPass
FIL-13Rejects file over 10 MBPOST /files?dryRun=true400400260 msPass
FIL-14Missing scope rejectedGET /files403403313 msPass
FIL-15Upload URL expires after 15 minPOST /filesexpiresAt +15mexpiresAt +15m136 msPass
FIL-16Download link worksGET /files/{id}/content200200189 msPass

Audit log 16/16 passed

IDTestEndpointExpectedActualTimeResult
AUD-01List audit logGET /audit-events200200242 msPass
AUD-02Pagination: page size and next cursorGET /audit-events?limit=2020 + cursor20 + cursor295 msPass
AUD-03Pagination: last page has no cursorGET /audit-events?cursor=…no cursorno cursor348 msPass
AUD-04Limit above 100 rejectedGET /audit-events?limit=500400400171 msPass
AUD-05Sort by creation date, descendingGET /audit-events?sort=-createdAtsortedsorted224 msPass
AUD-06Filter by actorGET /audit-events?actor=…matching items onlymatching items only277 msPass
AUD-07Filter by actionGET /audit-events?action=…matching items onlymatching items only330 msPass
AUD-08Filter by fromGET /audit-events?from=…matching items onlymatching items only153 msPass
AUD-09Get by idGET /audit-events/{id}200200206 msPass
AUD-10Unknown idGET /audit-events/{id}404404259 msPass
AUD-11Other tenant's idGET /audit-events/{id}404404312 msPass
AUD-12Response matches OpenAPI schemaGET /audit-events/{id}validvalid135 msPass
AUD-13Create (dry run)POST /audit-events?dryRun=true200200188 msPass
AUD-14Missing scope rejectedGET /audit-events403403241 msPass
AUD-15Invoice change recorded with actor and diffGET /audit-events?entity=invoice1 event1 event294 msPass
AUD-16Audit log is read-onlyDELETE /audit-events/{id}405405347 msPass

Sample report. Acme Ledger, its data, people and defects are fictional. Your report shows your product, your tickets and your tools.
More sample reports · Book a 15-minute call