Skip to main content

Zopa API Integration Guide

This guide will help your developers integrate the Zopa API (e-commerce widget, calculator widget, payments widget).

Hover over the keys section to generate your public and secret keys, if you haven't done so already.

Your secret key must remain secret at all times—never shared with anyone and never used in any frontend code. It is meant to be used only in server-side code to generate an HMAC signature.

Invalid signature: If Zopa returns 400 "Invalid signature", check: (1) Secret key has no leading/trailing spaces and is for the correct environment (sandbox vs live). (2) The exact same JSON string is used for the request body and for the HMAC input (base64 then HMAC-SHA256 with the secret key).


API Keys

KeyDescription
Your Public KeyThe public key you send with every HTTP request.
Your Secret KeyUsed to generate the HMAC signature. Never share it and never use it in frontend code.
Your DomainThe domain on which you intend to use the Zopa e-commerce solution. Required for the integration.
Webhook URLOptional. URL where we post status updates for your loan applications.
Payment Callback UrlCallback URL after the customer completes payment on their bank side. Configure this in the Zopa portal.

Client payload vs Zopa API payload

What you send to FinMatch finance-assistant is not the same as what we send to Zopa:

  • Client → finance-assistant: merchantID, environment, orderId, amount, verticalId, apr, term, deposit, deferredPeriod, productType, primaryApplicant, notification. (No publicKey – we look up the merchant and add it server-side.)
  • Finance-assistant → Zopa: publicKey (from merchant config), orderId, amount, verticalId, apr, term, deposit, deferredPeriod, productType, primaryApplicant, notification. (Same field names as the Zopa doc; we add publicKey and sign with the merchant secret.)

So your client payload is correct if it includes merchantID, environment, and the quote fields; we build the Zopa payload and HMAC on the server.

Payment Callback Url (Zopa portal)

Configure Payment Callback Url in the Zopa portal so that after the customer finishes payment on the bank side, Zopa redirects them to your site. This is separate from the optional Webhook URL (which is for server-to-server status updates).

What to use:

  • A URL on your domain that shows an order/application confirmation (e.g. thank-you page).
  • Example: https://www.finmatch.io/order-confirmation or https://www.finmatch.io/zopa/callback.
  • Zopa may append query parameters (e.g. orderId, applicationId, status); your page can read these and show the right order/status.

Where to set it: In the Zopa portal, in the same keys/config section as Your Domain and Webhook URL — paste the full URL (with https://) into the Payment Callback Url field and save.


HMAC Generation Guide

Step 1: Generate the HMAC signature

Create the base64 representation of your JSON object and generate the HMAC signature using the base64 string and your secret key.

Parameters:

  • data (Required) — Type: json — The JSON object of the loan product and customer data.
  • secret_key (Required) — Type: string — Your secret key generated above.

Example (PHP):

public static function getVendigoHeaderHash($data, $secret_key)
{
$base64 = base64_encode($data);
return hash_hmac('sha256', $base64, $secret_key);
}

Step 2: Create a signature header string

Add a new header to your request:

  • Signature (Required) — Type: string — Your HMAC signature from Step 1.

Create a Quote

Overview

Using this API endpoint you can create a new Zopa application.

Request

This endpoint accepts only POST requests.

POST https://app.zopa.demo.vendigo.com/v1/common/quote

Payload

Request body (JSON):

{
"publicKey": "Sqta2fk6i7pUNd1HhrgmOFdh4ynPGpVD",
"orderId": "ab2c",
"amount": 1250,
"verticalId": 19,
"apr": 0,
"term": 12,
"deposit": 375,
"deferredPeriod": 0,
"productType": "IFC",
"primaryApplicant": {
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"phone": "+442039650996"
},
"notification": {
"email": true,
"sms": true,
"message": "maximum 500 chars"
}
}

Fields:

FieldRequiredTypeDescription
publicKeyYesstringYour public key (generate at the top of this page).
orderIdYesstringA unique order ID or reference from your system.
amountYesfloatThe purchase amount. Deposit is deducted from this to create the loan amount.
verticalIdYesintegerThe ID of the vertical.
aprYesfloatThe product APR.
termYesintegerThe product term.
depositYesfloatThe deposit amount. Cannot exceed 50% of the purchase amount.
deferredPeriodYesintegerThe deferred period of the loan product.
productTypeYesstringThe product type.
primaryApplicantYesobjectPrimary applicant information.
primaryApplicant.firstNameYesstringPrimary applicant first name.
primaryApplicant.lastNameYesstringPrimary applicant last name.
primaryApplicant.emailYesstringPrimary applicant email.
primaryApplicant.phoneYesstringPrimary applicant phone number.
notificationYesobjectNotification settings.
notification.emailYesbooleanSet to true to receive email updates from Zopa.
notification.smsYesbooleanSet to true to receive SMS updates from Zopa.
notification.messageNostringOptional message when creating a quote.

The HMAC hash must be calculated from your payload and sent in the Signature header.

Signature: 453d64fcc70648f6671d9c21c4365be60ee4120721531f81a267315d19283db8
  • Signature (Required) — Type: string — HMAC signature generated from your payload.

Response

Example response:

{
"applicationId": 8958,
"orderId": "ab2c",
"status": "QUOTE",
"eSignUrl": null,
"refNumber": "96VI6S9U",
"refNumberLender": "81283951",
"referredReason": [],
"satisfactionNoteUrl": null,
"applicationUrl": "https://app.zopa.demo.vendigo.com/applications/continue-with-customer/beba37f52f13f5dada997ac5b7c811ff"
}
FieldDescription
applicationIdZopa's loan application ID.
orderIdYour reference number from your system.
statusLoan application status.
eSignUrlE-sign URL for the customer to sign loan contracts (Zopa or third-party).
refNumberZopa's reference number.
refNumberLenderLender's reference number (often empty at this step).
referredReasonIf the customer is referred, explains why and what they need to do; underwriter review may take some time.
satisfactionNoteUrlURL to the electronic SAT note the customer must sign if satisfied with the job.
applicationUrlZopa application URL.

Verification and testing

How to verify

  • Success: Zopa returns 200 with a JSON body containing applicationId, orderId, status (e.g. "QUOTE"), refNumber, and applicationUrl. Your integration is correct when you get this.
  • Invalid signature: Zopa returns 400 with a message like "Invalid signature". Fix: use the exact same JSON string for the request body and for the HMAC input, and ensure the secret key is correct (no spaces, correct environment).
  • Other 4xx/5xx: Check the response body for Zopa’s error message (e.g. validation, invalid key, rate limit).

Sandbox

  • Use the sandbox (demo) environment for testing: base URL https://app.zopa.demo.vendigo.com, and create quote with POST /v1/common/quote.
  • Use the public and secret keys from the Zopa portal for the sandbox environment (hover to reveal/generate).

Testing with curl

  1. Build the JSON payload (same key order as in this guide).
  2. Compute the signature: base64(JSON string) → HMAC-SHA256 with your secret key → hex.
  3. Send the request with the Signature header set to that hex value.

You can use the script below to generate the Signature header for a given payload, then call Zopa with curl:

# From finmatch-shared/cloud-run/finance-assistant:
node scripts/zopa-sign-payload.js '<json_payload>' '<secret_key>'
# Then use the printed Signature in curl:
curl -X POST https://app.zopa.demo.vendigo.com/v1/common/quote \
-H "Content-Type: application/json" -H "Signature: <hex_from_script>" \
-d '<json_payload>'

The payload must be the exact same string in both the script and -d (no extra spaces or key reordering).


Mark Job as Completed

Overview

This endpoint notifies customers that the job is completed and the "Satisfaction note" document can be signed.

Request

This endpoint accepts only POST requests.

POST https://app.zopa.demo.vendigo.com/v1/common/job-completed

Payload

{
"publicKey": "Sqta2fk6i7pUNd1HhrgmOFdh4ynPGpVD",
"refNumber": "96VI6S9U",
"tmLodgementNumber": 5001
}
FieldDescription
publicKeyYour public key (generate at the top of this page).
refNumberZopa's reference number for this loan application.
applicationIdZopa's loan application ID.
tmLodgementNumberThe Trustmark Lodgment number if the merchant has it.

Header

Include the HMAC signature as the Signature header (same process as above).

Signature: d7f6bc8633bca4103bac42abc60d59d3bcd7c721aad82577978cf042ba1f440f

Response

{
"applicationId": 8859,
"orderId": "ab2c",
"status": "QUOTE",
"eSignUrl": "https://puat-esign.bnpparibas-pf.co.uk/partner.aspx?PartnerRedirectId=59887707-4f8d-4435-9662-d641a1ea6246",
"refNumber": "M80DYQH8",
"refNumberLender": "81283951",
"referredReason": [],
"satisfactionNoteUrl": "https://app.pactsafe.com/sign?r=612de9744b57d309a9f04c47&s=5cb877478d04324965ff80c7&signature=...",
"applicationUrl": "https://app.zopa.demo.vendigo.com/applications/detail/357decab40567605505844d245d81c63"
}
FieldDescription
applicationIdZopa's loan application ID.
orderIdYour reference number.
statusLoan application status.
eSignUrlE-sign URL for the customer to sign loan contracts.
refNumberZopa's reference number.
refNumberLenderLender's reference number.
referredReasonExplains referral and next steps for the customer.
satisfactionNoteUrlURL to the electronic SAT note.
applicationUrlZopa application URL.

Get All Meta Data

Overview

Returns unique identifiers, constants, and enums used to manage Zopa applications.

Request

This endpoint accepts only GET requests.

GET https://app.zopa.demo.vendigo.com/v1/common/meta?publicKey=Sqta2fk6i7pUNd1HhrgmOFdh4ynPGpVD
  • publicKey — Your public key (generate at the top of this page).

Response

Response is unique per merchant and returns only valid data for the current merchant.

{
"currency": "GBP",
"language": "en-gb",
"deposit": {
"minPercent": 0,
"maxPercent": 50
},
"termsAndConditions": "https://www.vendigo.com/docs/customer-terms/",
"privacyPolicy": "https://www.vendigo.com/docs/privacy-policy/",
"vertical": [
{ "id": 19, "name": "Renewables" }
],
"product": [
{ "apr": 0, "term": 12, "deferredPeriod": 0, "type": "IFC" },
{ "apr": 0, "term": 24, "deferredPeriod": 0, "type": "IFC" },
{ "apr": 0, "term": 36, "deferredPeriod": 0, "type": "IFC" },
{ "apr": 0, "term": 48, "deferredPeriod": 0, "type": "IFC" },
{ "apr": 0, "term": 60, "deferredPeriod": 0, "type": "IFC" },
{ "apr": 11.9, "term": 24, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 11.9, "term": 36, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 11.9, "term": 48, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 11.9, "term": 60, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 11.9, "term": 84, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 11.9, "term": 120, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 10.9, "term": 24, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 10.9, "term": 36, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 10.9, "term": 48, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 10.9, "term": 60, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 10.9, "term": 84, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 10.9, "term": 120, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 13.9, "term": 24, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 13.9, "term": 36, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 13.9, "term": 48, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 13.9, "term": 60, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 13.9, "term": 84, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 13.9, "term": 120, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 13.9, "term": 96, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 0, "term": 18, "deferredPeriod": 0, "type": "IFC" },
{ "apr": 9.9, "term": 18, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 9.9, "term": 24, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 9.9, "term": 36, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 9.9, "term": 48, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 9.9, "term": 60, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 9.9, "term": 84, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 9.9, "term": 96, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 9.9, "term": 120, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 5.9, "term": 18, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 5.9, "term": 24, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 5.9, "term": 36, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 5.9, "term": 48, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 5.9, "term": 60, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 5.9, "term": 84, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 5.9, "term": 120, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 10.9, "term": 18, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 10.9, "term": 96, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 11.9, "term": 18, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 11.9, "term": 96, "deferredPeriod": 0, "type": "IBC" },
{ "apr": 13.9, "term": 18, "deferredPeriod": 0, "type": "IBC" }
]
}
FieldDescription
currencyCurrent currency in the Zopa system.
languageCurrent language (e.g. en-gb).
deposit.minPercentMinimum deposit percentage for an application.
deposit.maxPercentMaximum deposit percentage for an application.
termsAndConditionsURL to terms and conditions.
privacyPolicyURL to privacy policy.
verticalList of verticals (each with id, name).
productList of products (each with apr, term, deferredPeriod, type).

Application Status Updates

You can call the /status endpoint to get application updates. The HMAC signature must be sent in the HTTP header. Send your public key and the application ID in the JSON payload.

Example (cURL):

curl --request POST \
--url https://app.zopa.demo.vendigo.com/v1/loan-application/status \
--header 'Content-Type: application/json' \
--header 'Signature: The HMAC signature goes here' \
--header 'cache-control: no-cache' \
--data '{"publicKey":"Sqta2fk6i7pUNd1HhrgmOFdh4ynPGpVD","applicationId":1}'

Updates can also be pushed to the webhook URL you configured. In both cases the response has the following structure:

Response:

{
"applicationId": 1,
"orderId": "1",
"status": "APPROVED",
"eSignUrl": "https://trial.bonafidee.com/signsolo.aspx?t=F4E8D696-AD32-4E3D-B59D-7D6812C20B91",
"refNumber": "S16KYWRS",
"refNumberLender": "GNPDNCOK",
"referredReason": [],
"satisfactionNoteUrl": "https://app.pactsafe.com/sign?r=5cd2ee760ba7427150e9e4sdf8",
"applicationUrl": "https://app.zopa.demo.vendigo.com/applications/detail/357decab40567605505844d245d81c63",
"product": {
"productType": "IBC",
"apr": 9.90,
"term": 60,
"deferredPeriod": 0
}
}
FieldDescription
applicationIdZopa's loan application ID.
orderIdYour internal order ID or reference.
statusLoan application status.
eSignUrlE-sign URL for the customer (Zopa or third-party).
refNumberZopa's reference number.
refNumberLenderLender's reference number.
referredReasonReferral reason and next steps; may take time to update after underwriter review.
satisfactionNoteUrlURL to the electronic SAT note.
applicationUrlZopa application URL.
productThe product associated with the loan (productType, apr, term, deferredPeriod).