Skip to main content

Partner webhooks

Admin → Partner webhooks (https://admin.finmatch.io/partner-webhooks/). Requires admin:partner-webhooks. Without that grant the left-nav item is hidden and a direct URL is refused. James grants it by hand in auth-credentials.json (same as admin:applications); sign out and back in afterwards. Role admin passes the server check, but the sidebar still needs the grant. Catalogue label: Partner webhooks. See Users.

H1 Partner webhooks. Subtitle: Set where each partner receives credit status notifications, which merchants' applications they're told about, and the keys we sign them with.

This page configures push notifications. It is not the Partner API quote path. Visible copy says notification, not event. Do not paste signing secrets or partner API keys onto this public page.

Hint under Reload: Changes save straight away and take effect within a minute.

Banner when push is switched off on finance-assistant (PARTNER_WEBHOOKS_ENABLED): Credit status notifications are switched off on finance-assistant (PARTNER_WEBHOOKS_ENABLED), so test notifications and the lookup are unavailable. You can still prepare settings.

Load failure: Failed to load partner webhooks: plus the error. Empty partner list: No Partner API partners found. Badge on a partner with no settings yet: Not set up. Configured and sending: Enabled. Configured and paused: Off. Unavailable tests: Not available: credit status notifications are switched off on finance-assistant. If the Partner API key list cannot be read: The Partner API key list could not be read, so only partners that already have settings are listed.

Partner picker​

Partner dropdown lists Partner API partners. Reload re-reads settings and the merchant list. Pick a partner before editing the cards below.

Where notifications go​

Card Where notifications go. Intro: Turn notifications on or off, and set the test and live addresses we send to. When nothing is stored yet: This partner isn't set up yet. Saving creates its settings, with notifications switched off.

  • Enabled: send this partner credit status notifications for the merchants below.
  • Sandbox endpoint — placeholder https://partner.example/finmatch/sandbox
  • Live endpoint — placeholder https://partner.example/finmatch
  • Email copies to (optional) — every notification, including tests. — placeholder ops@partner.example. One address per line, comma, or semicolon; at most 10. Invalid save: An address under "Email copies to" is not a valid email address (or there are more than 10), so nothing was saved.

Endpoints are checked while typing (once past https://, or to clear an error as it is fixed), on blur, and again on Save settings. Messages appear under the field:

  • Must start with https://
  • Remove the query string (?…)
  • This address isn't reachable from the internet (private, loopback, link-local, metadata, .internal and .local hosts)
  • Enter a valid web address
  • Remove the username and password from the address
  • Remove the #… part from the address

The invalid field gets a red border (aria-invalid). Save settings is disabled while either endpoint is invalid. The server stays the source of truth: if it rejects an address, that message appears under the named field, the typed value stays, and the status reads Not saved. Fix the address above.

Which merchants they hear about​

Card Which merchants they hear about. Intro: The partner is only told about applications for merchants listed here.

Columns: Merchant, Granted, Granted by, Consent, then Remove. Empty: No merchants yet. The partner gets no notifications until you add one.

Add row: searchable Merchant combobox (listbox Merchants, placeholder Search by name or id, e.g. M000106). Results show Name · M000106. Linked merchants are left out. Idle chosen line: No merchant chosen. After a pick: Merchant id: plus the id (that id is what is submitted). Empty merchant-api list: The merchant list is empty, so there is no merchant to add. Load failure: Couldn't load the merchant list plus the error, then Click Reload to try again. While fetching: Loading merchants… No matches: No merchant matches, or it is already on this partner's list. Empty search when every merchant is already listed: Every merchant is already on this partner's list. Keyboard: ArrowUp / ArrowDown, Enter chooses, Escape closes, Tab moves on.

Consent note: how the merchant agreed in writing — placeholder Written consent by email from Jo Bloggs, 27 Sep 2026. Add merchant. No merchant chosen: Choose a merchant from the list first. Empty consent: Note how the merchant agreed before adding them.

The list is GET /api/merchants (same as the Merchants page). The server still refuses an id that is not in merchants.json.

Keys we sign with​

Card Keys we sign with. Intro: The partner uses these to check a notification really came from us. Each key is shown once.

Columns Sandbox and Live. Empty environment: No key. Notifications in this environment are not sent. Generate new key (disabled with title Retire a key first when two keys already exist). Retire removes an old key after the partner has moved.

Hint: Up to two keys per environment. With two, both sign every notification, so the partner can switch to the new key before you retire the old one.

Generate asks for the admin password (Enter your admin password to generate a signing key:). The secret appears once in an amber box: Copy this secret now. It will not be shown again. Buttons Copy and I have stored it. Copy success: Copied. After I have stored it and Reload, the secret is gone. Do not paste the value into /docs/.

Rotation: generate a second key (both sign), the partner moves to it, then Retire the old one.

Send a test notification​

Card Send a test notification. Intro: Pick a lender status and send a clearly marked test. Nothing real is affected.

The sample is a form, not raw JSON. Editable: Merchant, Merchant reference, and Lender status to simulate. Lender is a dropdown; Zopa only for now. Loan amount (£) for Zopa is not an editable box: operators see Zopa does not send a loan amount. (A lender that does send an amount would show the two-decimal pounds field, default 9000.00; invalid: Enter pounds with two decimals, e.g. 9000.00; Must be more than 0.00. Fallback line if that lender still has no amount: This lender does not send a loan amount.) Environment comes from the button you press. Prefill: first merchant Linked to this partner, else the first Other merchants row (Send stays disabled until you pick a linked one); Merchant reference QB-10452.

  • Merchant — optgroups Linked to this partner then Other merchants (or Merchants when none are linked). Only linked merchants (the same grant list as Which merchants they hear about, with sharing on) can be sent. Other merchants stays in the list for reference; choosing one shows Choose a merchant linked to this partner and Send sandbox test / Send live test stay disabled until a linked merchant is picked. Default is the first linked merchant, else the first other (for example M000106 when it is not on this partner). Not in the directory: That merchant is not in the merchant list. Server 400 for an unlinked merchant uses the same field error; nothing is POSTed and no partner email copy is sent.
  • Merchant reference — default QB-10452. Invalid: Letters, digits, dots, dashes and underscores only, up to 64.
  • Lender — dropdown, Zopa only for now.
  • Lender status to simulate — filled from the server. Options are every raw Zopa code, by code only, for example CONDITIONALLY_APPROVED. No friendly labels. Simulated status on the result shows the same code. Unknown scenario: That status is not one of the test choices. Idle fallback: Plain test (no status).
  • Loan amount (£) — for Zopa, hidden input and the line Zopa does not send a loan amount. A crafted sample that still sends loanAmount is refused: This field cannot be set.

Too long: Too long. Extra field: This field cannot be set. Server 400: Some sample values are not valid. See the fields above. plus the per-field messages. Nothing is sent or emailed while the sample is invalid.

Send sandbox test / Send live test are disabled while a sample field is invalid, while the chosen merchant is not linked to this partner, and when the partner is not configured or tests are unavailable.

Fixed in every test: event_type: credit_application.test, event_id: generated, timestamp: when sent, finmatch_reference: TEST-{merchant}-{partner}-R00000001 (or TEST-NOT-A-REAL-APPLICATION with no merchant/partner), lender_application_id: 8859 (typical historic Zopa shape; changes with lender), FinMatch-Test: true. Lender application id is not a boxed field. The TEST- reference can never parse as a minted reference. The test never writes the Applications ledger.

Result fields: Environment, Simulated status, Status, HTTP status, Time taken, Notification id, Signed with, Email copy (and Error class / Reason on failure).

Email copy lines: Emailed to N addresses, marked TEST.; Email to N addresses failed. The test itself is unaffected.; No addresses under "Email copies to".; Not emailed: one of the addresses is not valid, so nobody was emailed.; Email copies are switched off on finance-assistant.; fallback Not emailed.

What we sent then shows the target URL, Request headers (FinMatch-Signature, FinMatch-Key-Id, FinMatch-Test, Content-Type and the rest — the signature is an HMAC, never a secret) and Body. Note: Shown formatted. Copy gives the exact bytes that were signed. What came back: HTTP status, time taken, and Response body (First 1 KB only. when truncated). Empty partner reply: No response body. Each block has Copy.

The signed body is eleven keys, in this order: event_id, event_type, timestamp, environment, merchant_id, merchant_reference, finmatch_reference, lender, lender_application_id, lender_status, loan_amount. Partners match a notification with merchant_reference and/or finmatch_reference. Quote id (data.finmatch_quote_id) stays on the Partner API quote response. lender_status is the lender's raw code as a string, for example "CONDITIONALLY_APPROVED". Not { code, description }. Created events use null. Tests of a Zopa status use the same string. For Zopa, loan_amount is null (the test of a Zopa status does not invent an amount). Tests mark event_type credit_application.test. The sample POST carries merchant and merchant reference; lender application id and lender are set server-side. Zopa omits loanAmount from the sample.

Addresses under Email copies to (including tests) get a copy of that envelope, not a second notification.

Subject (tests): [TEST] Credit Status Notification: {merchant_reference} | Status: {lender_status} with the raw lender code, for example CONDITIONALLY_APPROVED (not “Conditionally Approved”). Live copies drop the [TEST] prefix. If merchant_reference is empty, the subject uses finmatch_reference. It does not invent QB-10452. The admin test form still prefills Merchant reference QB-10452.

Body is one labelled row per envelope key, in that same eleven-key order: Event ID, Event type, Timestamp, Environment, Merchant ID, Merchant reference, FinMatch reference, Lender, Lender application ID, Lender status code, Loan amount.

  • Event type is the JSON string (credit_application.test / credit_application.status_changed).
  • Merchant ID is the id only. No company name in parentheses.
  • Lender status code is the raw code (for example CONDITIONALLY_APPROVED). There is no description row.
  • For Zopa, JSON loan_amount stays null. The email row is Loan amount: Null (Zopa does not send a loan amount) — not a dash. The test-form note is still Zopa does not send a loan amount (no “Null”).

There is no Delivery result row and no footer that mentions Also email each notification to.

Partner webhook deliveries​

Card Partner webhook deliveries. Intro: Look up signed partner webhook deliveries for one FinMatch reference. This is not CSN email and not the Applications ledger. Only partner-attributed references (not P000000) get a delivery row.

This card is the staff view of partner_webhook_deliveries. It is a partner feature surfaced in admin, not an admin-wide log of every FinMatch reference. CSN emails and on-demand test notifications do not appear here.

Reference placeholder e.g. M000106-P000001-R00004012, then Look up.

Columns: Type, Partner, Env, Status, Attempts, Last HTTP, Last error, Last attempt.

Empty states:

  • Lookup failed. We could not read partner webhook deliveries for this reference.
  • This application never notified a partner. P000000 means no partner (FinMatch-direct), so no partner webhook delivery is created. CSN emails and the Applications ledger are separate.
  • This application never notified a partner. P000000 means no partner (FinMatch-direct), so no partner webhook delivery is created. We also have no Applications ledger row for this reference — check the typing. CSN emails are separate.
  • No partner webhook deliveries for this reference. It does not look like a minted FinMatch reference, so a partner webhook would not have been planned.
  • No partner webhook delivery row for this reference. A skipped send (partner off, merchant not granted, no key) would show here as skipped. Empty means none was planned: webhooks off at the time, a history replay, or a same-status redelivery. This is not CSN email.
  • No partner webhook delivery row, and no Applications ledger row for this reference. If a partner was notified, we cannot find it here. Check the reference, or use Applications for the ledger and CSN email.

Replay of dead deliveries is not on this page.