Skip to content

Integrations and platforms

API and webhook testing

API and webhook testing verifies that a fintech platform's public and partner APIs match their published specification, deduplicate retried requests, sign and redeliver webhooks correctly, and keep older client versions working. Buyers receive a contract test suite, an idempotency and retry behaviour report, webhook signature, ordering, and replay checks, and versioning and backward compatibility checks.

What API and webhook testing covers

A defect in a platform API repeats in every integrator's money flow. A retried payout that the API does not deduplicate pays twice, and a webhook that never arrives leaves an invoice open or a card unfrozen at every client that relies on the event. We test the public and partner APIs of platforms that other fintechs build on, such as banking-as-a-service platforms, acquirers, open banking providers, KYC services, billing, chargeback, and payroll platforms.

Contract tests from the specification

The suite is generated from your OpenAPI document and runs every documented operation. Since OpenAPI 3.1.0 (15 February 2021), a top-level webhooks field describes incoming webhooks that the API consumer may choose to implement, so event payloads get the same schema checks as responses. The current version is OpenAPI 3.2.1, published 10 September 2026.

Checks per operation:

  • every documented status code is reachable, and no undocumented status code or field appears;
  • required fields, types, formats, and enum values match, including amounts as integer minor units or decimal strings as documented;
  • invalid requests return the documented 4xx error body, not a 500;
  • pagination, filtering, and tenant scoping return only the caller's records.

A response that drifts from the schema breaks client SDKs generated from that schema. The contract versus end-to-end comparison shows where each test type catches it.

Idempotency keys and retries

RFC 9110 section 9.2.2 does not list POST as idempotent, so a payment or transfer POST needs an application-level key. The IETF Idempotency-Key header draft 07 of 15 October 2025 expired on 18 April 2026 and is not an RFC (checked 2026-09-16). We test against your documented rule and use the draft as the reference where your rule follows it. Test cases:

  • Retry after completion. The same key and body return the stored result, success or error, with one side effect (section 2.6).
  • Key reused with another body. Section 2.2 says the key MUST NOT be reused with a different payload; section 2.7 says the resource SHOULD reply 422.
  • Concurrent retry. A retry sent while the original is still processing SHOULD receive 409 (section 2.7). We send parallel requests to find races that create two records.
  • Missing key. An operation documented as requiring the key SHOULD return 400 with a link to documentation (section 2.7).
  • Expiry and scope. Section 2.5.2 says resources MUST publish an idempotency specification that includes expiration policy if applicable. We measure the real expiry window and confirm that one tenant's key never returns another tenant's response.

Retries also cover timeouts where the server commits after the client disconnects, and 429 or 503 responses with Retry-After. The payment idempotency testing guide shows the request sequences.

Webhook signatures, ordering, and replay

We test your documented scheme. For platforms that adopt the Standard Webhooks specification (checked 2026-09-16), the checks follow it:

  • webhook-id, webhook-timestamp, and webhook-signature headers on every event;
  • a signature over msg_id.timestamp.payload with v1 HMAC-SHA256 or v1a Ed25519, computed on the raw body;
  • two space-delimited signatures during secret rotation, with both verifying;
  • a redelivery that keeps the same webhook-id, which the specification recommends consumers use as an idempotency key;
  • any non-2xx response, including 3xx, counts as a failure to retry, and after 410 Gone the sender should disable the endpoint.

Ordering tests deliver a refund event before its capture event, and a stale status after a newer one. Each event must carry enough data, such as a resource version or timestamp, for a consumer to discard the stale one. The webhook reliability guide covers receiver-side tests.

Versioning and backward compatibility

The released and candidate specifications are compared operation by operation. Breaking changes include a removed field, a new required request field, a narrowed enum, a changed type, and a changed default. A new enum value in a response is flagged against your version policy, because strict clients reject unknown values. Recorded requests from each supported SDK version replay against the candidate build. Deprecated operations are checked for the Deprecation header of RFC 9745 and the Sunset header of RFC 8594; RFC 9745 section 4 says the Sunset timestamp MUST NOT be earlier than the Deprecation timestamp.

Deliverables

  • Contract test suite generated from the API specification

    An automated suite built from your OpenAPI document that sends valid and invalid requests to every documented operation and fails when a status code, header, field, type, or enum value differs from the specification.

  • Idempotency and retry behaviour report

    A report with one row per state-changing operation that records the documented idempotency rule, the observed response to a repeated, altered, concurrent, and expired key, and the count of side effects, for engineering leads and integrating partners.

  • Webhook signature, ordering, and replay checks

    Automated checks that verify each event type's signature, timestamp, event identifier, redelivery after a failed response, and behaviour on duplicate, late, and out-of-order delivery against your documented webhook scheme.

  • Versioning and backward compatibility checks

    A comparison of the released and candidate API specifications and a replay of requests from supported client versions that lists each breaking change, its affected operations, and the deprecation and sunset headers returned.

How API tests fit your release process

Pull requests. The contract test suite runs in your CI against the service built from the branch. A specification diff runs in the same stage, and a pull request that removes a field or narrows an enum without a version change fails with the operation and the breaking change named.

Staging, nightly and before release. Idempotency tests need the real datastore and a fault-injecting proxy that drops responses after the server commits, so they run in staging. The idempotency and retry behaviour report is regenerated each run, and a new operation without a documented key rule appears as an open row.

Webhook receiver. A test receiver in staging records every delivery and returns scripted responses: 500, 3xx, 410, and a response slower than your delivery timeout. Webhook signature, ordering, and replay checks read its log, so retry intervals and event identifiers are compared per attempt.

Before each version release. Versioning and backward compatibility checks replay recorded traffic from each SDK version you still support. Your team decides whether a flagged change ships as a new version or waits for the sunset date.

After release. A smoke run on a sandbox tenant in production sends one idempotent request twice and receives one signed event, and confirms the deployed specification matches the tested one.

Test data. Tests use synthetic tenants, API keys, and webhook secrets. Recorded traffic from production can contain account data and personal data, so it is masked under the procedures on our security page before replay.

Evidence and handover. The suites live in your repository and run without us. CI run logs with the commit, the result, and the approver serve as testing evidence for change management under SOC 2 criterion CC8.1, described on the SOC 2 page. Your team owns the specification, the version policy, webhook secrets, and the release decision.

Sample deliverable

Sample, synthetic data

integration coverage reportIntegration coverage report: bank connectors, terminal SDK, broker API, and payout partner
Integration coverage report: bank connectors, terminal SDK, broker API, and payout partner
ConnectorHappy pathTimeout and retryExpired consent or tokenDuplicate, out-of-order, or offline deliveryCoverage status and open defect
Bank connector 01 (Berlin Group NextGenPSD2)Covered, 9 of 9 cases passed: POST /v1/consents reaches consentStatus valid after SCA, and the accounts, balances, and transactions calls return 200 for that consentCovered, 6 of 6 cases passed: a timed-out transactions call is retried with a new X-Request-ID, and a 429 ACCESS_EXCEEDED response stops refreshes for that consent until the next dayFailed, 4 of 5 cases passed: after a 401 CONSENT_EXPIRED response, the connector keeps scheduling refreshes and shows a stale balance of 1,240.00 XTS as currentCovered, 5 of 5 cases passed: a pending transaction that returns with bookingStatus booked and a new transactionId replaces the pending entry, and overlapping dateFrom windows import no duplicatesFailed, retest scheduled after the INT-0203 fix. INT-0203 (medium): the bank connector does not set reconnect required after a 401 CONSENT_EXPIRED response
Bank feed 03 (OFX)Covered, 7 of 7 cases passed: a STMTRQ for a checking account returns STMTRS with SONRS STATUS code 0, and each STMTTRN posts once to the ledger bank accountCovered, 4 of 4 cases passed: a statement request retried after a timeout reuses its TRNUID, and status 2019 Duplicate request is handled as already processedCovered, 3 of 3 cases passed: SONRS status 15500 Signon invalid stops the bank feed and asks the user to reconnect, with no empty statement importedFailed, 5 of 6 cases passed: overlapping DTSTART and DTEND windows repeat FITID values, and one repeated FITID with a changed TRNAMT imports as a second transactionFailed, retest scheduled after the INT-0221 fix. INT-0221 (medium): a repeated FITID with a changed TRNAMT of 18.75 XTS imports as a second bank transaction
Card terminal SDK (offline mode)Covered, 12 of 12 cases passed: online EMV contact and contactless sales return tag 8A value 00, and each approved sale posts one captureCovered, 6 of 6 cases passed: an authorisation request with no host response before the timeout sends a reversal before the sale is retriedCovered, 4 of 4 cases passed: a terminal session token that expires while the terminal is offline is refreshed before the store-and-forward upload startsFailed, 10 of 11 cases passed: offline sales under the terminal floor limit (tag 9F1B) complete with tag 8A value Y3, and one retried store-and-forward upload posts two capturesBlocked for release until the INT-0214 fix passes retest. INT-0214 (high): a store-and-forward upload retried after a lost acknowledgement captures one 42.50 XTS sale twice
Broker API A (FIX 4.4)Covered, 10 of 10 cases passed: Logon (35=A) succeeds, and each ExecutionReport (35=8) with ExecType (150) F updates the imported position onceCovered, 5 of 5 cases passed: no inbound message within HeartBtInt (108) triggers a TestRequest (35=1), and a reconnect resumes at the next expected MsgSeqNum (34)Covered, 3 of 3 cases passed: a Logon with an expired Password (554) is answered with Logout (35=5) and Text (58), and the broker link shows as disconnected with positions keptCovered, 8 of 8 cases passed: a MsgSeqNum (34) gap triggers ResendRequest (35=2), and a resent ExecutionReport with PossDupFlag (43) Y and a known ExecID (17) is not counted as a second fillCovered, with no open defects on Broker API A
Payout partner B (ISO 20022 pacs.008)Covered, 8 of 8 cases passed: a pacs.008.001.08 credit transfer with a UETR returns pacs.002 TxSts ACSP and then ACSC, and the payout is marked settled onceFailed, 5 of 6 cases passed: a timed-out pacs.008 submission is followed by a pacs.028 status request, but one retry is sent as a new pacs.008 with a new UETRCovered, 4 of 4 cases passed: a payout submitted with an expired API access token returns 401, and the token is refreshed before one resubmission with the same UETRCovered, 6 of 6 cases passed: a pacs.002 TxSts RJCT after ACSP releases the payout hold once, and a repeated pacs.002 for the same UETR changes no balanceBlocked for release until the INT-0230 fix passes retest. INT-0230 (high): a pacs.008 retried after a timeout carries a new UETR and pays 250.00 XTS twice to one beneficiary

Amounts use XTS, the ISO 4217 code reserved for testing. Connectors, banks, and partners are generic sandbox endpoints, and terminal sales use scheme test cards masked to BIN and last four.

Questions

What is API contract testing?

API contract testing checks that an API's requests and responses match an agreed specification, usually an OpenAPI document, for each operation: paths, methods, status codes, headers, required fields, types, and enum values. A contract test fails when the provider changes a response that a consumer depends on, before the change reaches the consumer.

What is an idempotency key?

An idempotency key is a unique client-generated value, sent in a request header such as Idempotency-Key, that lets an API recognise a retry of the same request and return the first result instead of repeating the operation. IETF draft-ietf-httpapi-idempotency-key-header-07 says the key MUST NOT be reused with a different request payload.

How to test webhooks?

Test webhooks against a recording endpoint that you control. Verify the signature and timestamp of each event type, then return failure responses to confirm retries keep the same event identifier. Send duplicate, late, and out-of-order events to confirm the receiver processes each event once and ignores stale state.

Scope your API and webhook testing

Send your OpenAPI document, webhook event list, and version policy, and we return a test scope per operation and event type.

Scope your testing