PartnerPage webhooks tell your own systems the moment something happens in your partner directory: a new lead comes in, a lead changes status, a partner applies or submits a profile. Each notification is an HTTPS POST with a JSON body to a URL you choose, sent within seconds of the event.
Typical uses are pushing new leads into your CRM, posting to a Slack channel, or starting an automation in n8n, Zapier or Make. This guide is written for the person building the receiving end. It covers the events you can subscribe to, the exact request we send, how to secure your endpoint, what to reply, and the full payload of every event.
Before you start
Webhooks are enabled per directory by PartnerPage. When they are on, Webhooks appears in the left menu of your directory in the dashboard. If you do not see it, ask your PartnerPage contact.
Your endpoint must be reachable from the public internet over HTTPS.
Team members need the Webhooks permissions (Events and Logs) in their role to see and manage subscriptions.
Set up a subscription
Open your directory in the PartnerPage dashboard and choose Webhooks, then Events.
Click Create new event.
Give the subscription a Name for your own reference, for example "New leads to HubSpot".
Enter the Subscriber URL: the HTTPS address we should call.
Pick the Event trigger you want to receive. The Event data sample on the page shows an example payload for the trigger you picked.
Save.
Each directory can have one subscriber URL per event trigger. To send the same event to several systems, point the subscription at one endpoint (an n8n or Zapier flow, or your own service) and fan out from there. You can edit or delete a subscription from the Events list at any time.
Events
Event trigger ( | Sent when |
| A new lead is created for a partner: a visitor submits the contact form on a partner profile, or your team assigns a matchmaking request to a partner (one event per partner). |
| The lead's state changes (accepted, declined or closed) or its project status changes (new, contacted, engaged, secured, launched/deployed or lost). One event per change. |
| A visitor submits your directory's matchmaking form ("help me find a partner"). |
| Your team assigns the matchmaking request to one or more partners, or sets or clears its owner. |
| Someone submits your partner application form. Requires partner applications to be enabled on the directory. Not sent for applications created through the API. |
| Your team accepts the application (a partner profile is created and its id and title are in the payload) or declines it. Not sent when this is done through the API. |
| A partner completes the self-service Get Listed flow, creating a listing that waits for your approval. |
| A partner submits their profile, new or edited, for your review. |
The request we send
Every delivery is an HTTP POST to your subscriber URL with a JSON body encoded as UTF-8. Delivery is asynchronous: it happens within seconds of the event, from PartnerPage's servers.
Header | Value |
|
|
| The event trigger, for example |
| HMAC-SHA256 of the raw request body, hex encoded, keyed with your webhook signing secret (see "Securing your endpoint") |
Conventions used in every payload:
Ids are UUIDs, sent as strings.
Timestamps such as
createdare in UTC, formattedYYYY-MM-DD HH:MM.custom_fieldslists the answers to the custom questions on your form. Each item hasexport_label(the label you gave the field),internal_labelandtext_value(the answer as text; multiple choices are joined into one string). Standard fields such as name and email are not repeated here.Fields with no value are
null.We may add fields over time. Ignore fields you do not recognise rather than rejecting the request.
Securing your endpoint
Use HTTPS, then apply both of the following.
Use a URL nobody can guess. Include a long random token in the path or query string of your subscriber URL, for example https://hooks.example.com/partnerpage/8f3c1d…b2a1, and reject requests that do not carry it. n8n, Zapier and Make webhook URLs already include such a token.
Verify the signature. X-Webhook-Signature is the lowercase hex HMAC-SHA256 of the exact bytes of the request body, using your webhook signing secret as the key. Compute it on your side from the raw body, before any JSON parsing, and compare using a constant-time comparison. Your directory's signing secret is provided by your PartnerPage contact.
Python:
import hashlib import hmac def is_valid(raw_body: bytes, header_signature: str, secret: str) -> bool: expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, header_signature)Node.js:
const crypto = require("crypto"); function isValid(rawBody, headerSignature, secret) { const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex"); return ( expected.length === headerSignature.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(headerSignature)) ); }In frameworks that parse JSON automatically (Express with express.json(), Django REST Framework, Rails), make sure you sign the original body bytes, not a re-serialised version of the parsed object.
Responding to a delivery
Reply with any
2xxstatus within 15 seconds. The body of your response is ignored; its first 255 characters are stored in the delivery log.Do slow work after replying: accept the request, queue it, return
200.A delivery that times out, cannot connect, or receives a status outside
2xxis recorded as failed and is not retried. Check the Logs page regularly, and use the ids in the payload to look up anything you missed in the dashboard.Events for the same record can arrive close together, for example a
contact_request_createdfollowed by acontact_request_updated. Do not assume order; treat thestateandproject_statusfields in the payload as the source of truth.Deliveries are normally sent once. If you ever receive the same event twice, the record id together with its state lets you ignore the repeat.
Delivery logs
Webhooks, then Logs, lists every delivery attempt with its timestamp, the subscription name, the event trigger and the HTTP status we received. Open an entry to see the response text and the exact payload we sent. Use the errors filter to show only failed deliveries.
Statuses recorded for failures that never reached your server:
Recorded status | Meaning |
| Your endpoint did not answer within 15 seconds |
| We could not connect (DNS, TLS or network error) |
| Another unexpected error while sending |
Any other status is the one your endpoint returned.
Troubleshooting
No log entry at all. Check that a subscription exists for that trigger and that the action happened in this directory. Partner applications handled through the API do not send webhooks.
408. Reply first, process later.
503. The URL must resolve publicly and present a valid TLS certificate. Localhost, private networks and self-signed certificates do not work; use a tunnel such as ngrok while developing.
401 or 403. Your endpoint is asking for authentication. We send no authentication header; put a token in the URL instead.
404 or 405. Check the path and that the endpoint accepts
POST.
Payload reference
Lead object (contact_request)
Sent inside contact_request_created and contact_request_updated.
Field | Meaning |
| Lead id |
| When the lead was created (UTC) |
|
|
|
|
| The lead's contact details |
| The lead's company |
| The message the lead wrote |
| Consent checkboxes on the form: |
| UTM parameters captured on the page, or |
| Id of the submitted form response |
|
|
| Id of the matchmaking request the lead came from, or |
| The partner profile that received the lead: |
|
|
| Set when the partner declined: |
| Set when the lead was closed: |
| Filter answers from older directories, usually |
contact_request_created
Field | Meaning |
| Partner profile id |
| Directory id |
| The lead object above |
| Custom form answers |
| Link to the lead in your dashboard |
{ "entry_id": "10c83cb2-ec23-49c5-b1c1-36ad2c5f5744", "directory_id": "a6736e4f-ad5b-4cc9-856b-9f3bf6688a79", "contact_request": { "id": "58e8eba8-ce91-4ab7-870a-0c33108b7101", "created": "2026-09-28 16:19", "state": "pending", "project_status": null, "first_name": "Jon", "last_name": "Smith", "email": "jon.smith@example.com", "phone": "8322787786", "company_name": "Custom Water Filters", "company_website": "http://www.customwaterfilters.com/", "terms": true, "privacy": true, "share": null, "comments": "Wanting to know how to design a new diagram", "utm_query_params": null, "custom_form_response_id": "4a552c1a-626f-484f-97d7-88cff5bfc6b7", "entry": { "id": "10c83cb2-ec23-49c5-b1c1-36ad2c5f5744", "title": "ContractorBI by TopBuilder", "owner": "6ba56da9-a8ae-4d84-9337-cedabefdcdc6" }, "directory": { "id": "a6736e4f-ad5b-4cc9-856b-9f3bf6688a79", "name": "Acme Technology Partner Ecosystem", "org_id": "852231d3-ec36-49d1-af94-e50173d172aa", "org_name": "Acme", "contact_request_project_statuses_enabled": true }, "source": "contact_request", "match_origin": null, "decline_reason": null, "decline_reason_detail": null, "close_reason": null, "close_reason_detail": null, "legacy_filter_responses": null }, "custom_fields": [ { "export_label": "Desired timeline", "internal_label": "desired_timeline", "text_value": "Q2" } ], "directory_contact_request_url": "https://directory.partnerpage.io/directories/a6736e4f-ad5b-4cc9-856b-9f3bf6688a79/directory/contact-requests/58e8eba8-ce91-4ab7-870a-0c33108b7101" }
contact_request_updated
Same fields as contact_request_created. state, project_status and, where relevant, the decline or close reason fields carry the new values. For example, after a partner declines the lead:
{ "entry_id": "10c83cb2-ec23-49c5-b1c1-36ad2c5f5744", "directory_id": "a6736e4f-ad5b-4cc9-856b-9f3bf6688a79", "contact_request": { "id": "58e8eba8-ce91-4ab7-870a-0c33108b7101", "created": "2026-09-28 16:19", "state": "declined", "project_status": null, "decline_reason": "not_a_fit", "decline_reason_detail": "We only work with companies in the EU.", "close_reason": null, "close_reason_detail": null, "first_name": "Jon", "last_name": "Smith", "email": "jon.smith@example.com", "phone": "8322787786", "company_name": "Custom Water Filters", "company_website": "http://www.customwaterfilters.com/", "terms": true, "privacy": true, "share": null, "comments": "Wanting to know how to design a new diagram", "utm_query_params": null, "custom_form_response_id": "4a552c1a-626f-484f-97d7-88cff5bfc6b7", "entry": { "id": "10c83cb2-ec23-49c5-b1c1-36ad2c5f5744", "title": "ContractorBI by TopBuilder", "owner": "6ba56da9-a8ae-4d84-9337-cedabefdcdc6" }, "directory": { "id": "a6736e4f-ad5b-4cc9-856b-9f3bf6688a79", "name": "Acme Technology Partner Ecosystem", "org_id": "852231d3-ec36-49d1-af94-e50173d172aa", "org_name": "Acme", "contact_request_project_statuses_enabled": true }, "source": "contact_request", "match_origin": null, "legacy_filter_responses": null }, "custom_fields": [], "directory_contact_request_url": "https://directory.partnerpage.io/directories/a6736e4f-ad5b-4cc9-856b-9f3bf6688a79/directory/contact-requests/58e8eba8-ce91-4ab7-870a-0c33108b7101" }
matchmaking_request_created
Field | Meaning |
| Matchmaking request id |
| Your directory and organization |
| Custom form answers |
| The request: |
{ "request_id": "3a7a1b6d-9d12-4b5f-9b1b-2a0d6f2e2c9b", "directory_id": "827a617e-eb92-410d-b121-cf80e9023675", "directory_name": "Acme Partner Directory", "directory_owner_name": "Acme", "custom_fields": [ { "export_label": "Desired timeline", "internal_label": "desired_timeline", "text_value": "Q2" } ], "request_details": { "id": "3a7a1b6d-9d12-4b5f-9b1b-2a0d6f2e2c9b", "created": "2026-09-28 16:19", "state": "new", "first_name": "Jon", "last_name": "Smith", "email": "jon.smith@example.com", "phone": "8322787786", "company_name": "Custom Water Filters", "company_website": "http://www.customwaterfilters.com/", "terms": true, "privacy": true, "share": false, "comments": "Looking for an implementation partner", "assigned_partners": [], "date_assigned": null, "custom_legal_text": null, "custom_legal_text_value": null, "owner": null, "owner_name": null, "utm_query_params": null, "custom_form_response_id": "4a552c1a-626f-484f-97d7-88cff5bfc6b7", "directory": { "id": "827a617e-eb92-410d-b121-cf80e9023675", "name": "Acme" }, "legacy_filter_responses": null } }
matchmaking_request_updated
Same fields as matchmaking_request_created. After your team assigns the request to two partners, request_details changes to:
{ "state": "assigned", "assigned_partners": [ { "partner_entry__id": "10c83cb2-ec23-49c5-b1c1-36ad2c5f5744", "partner_name": "ContractorBI by TopBuilder" }, { "partner_entry__id": "d8e7b6a5-4c3d-4210-9f8e-7d6c5b4a3210", "partner_name": "Custom Water Filters" } ], "date_assigned": "2026-09-28T16:25:03.120453+00:00", "owner": "be051b53-cc24-4b86-a790-0c04dc2d6d3d", "owner_name": "Alex Rivera" }Each assignment also creates a lead on the partner's profile, so a contact_request_created event with source set to matchmaking and match_origin set to this request id follows for every partner assigned.
partner_application_created
Field | Meaning |
| Application id |
| Your directory and organization |
| Custom form answers |
| The application: |
{ "application_id": "9f1c0f4e-1a2b-4c3d-8e5f-6a7b8c9d0e1f", "directory_id": "827a617e-eb92-410d-b121-cf80e9023675", "directory_name": "Acme Partner Directory", "directory_owner_name": "Acme", "custom_fields": [ { "export_label": "Partner type", "internal_label": "partner_type", "text_value": "Reseller" } ], "application_details": { "id": "9f1c0f4e-1a2b-4c3d-8e5f-6a7b8c9d0e1f", "created": "2026-09-28 16:19", "state": "pending", "first_name": "Jon", "last_name": "Smith", "email": "jon.smith@example.com", "company_name": "Custom Water Filters", "company_website": "http://www.customwaterfilters.com/", "custom_form_response_id": "4a552c1a-626f-484f-97d7-88cff5bfc6b7", "directory": { "id": "827a617e-eb92-410d-b121-cf80e9023675", "org_name": "Acme" } } }
partner_application_updated
Same fields as partner_application_created, plus entry_id and entry_title: the partner profile created when the application is accepted, or null when it is declined. application_details.state is accepted or declined.
{ "application_id": "9f1c0f4e-1a2b-4c3d-8e5f-6a7b8c9d0e1f", "directory_id": "827a617e-eb92-410d-b121-cf80e9023675", "directory_name": "Acme Partner Directory", "directory_owner_name": "Acme", "custom_fields": [ { "export_label": "Partner type", "internal_label": "partner_type", "text_value": "Reseller" } ], "application_details": { "id": "9f1c0f4e-1a2b-4c3d-8e5f-6a7b8c9d0e1f", "created": "2026-09-28 16:19", "state": "accepted", "first_name": "Jon", "last_name": "Smith", "email": "jon.smith@example.com", "company_name": "Custom Water Filters", "company_website": "http://www.customwaterfilters.com/", "custom_form_response_id": "4a552c1a-626f-484f-97d7-88cff5bfc6b7", "directory": { "id": "827a617e-eb92-410d-b121-cf80e9023675", "org_name": "Acme" } }, "entry_id": "d8e7b6a5-4c3d-4210-9f8e-7d6c5b4a3210", "entry_title": "Custom Water Filters" }
partner_get_listed_completed
Field | Meaning |
| Email of the partner user who completed Get Listed |
| Always |
| The listing created: |
| The Get Listed record: |
| Who submitted it: |
{ "customer_id": "founder@example.xyz", "event_name": "partner_get_listed_completed", "data": { "entry": { "id": "3245d51c-a7fc-4498-90a5-f3d8aeded8d9", "title": "Tiger Woods Labs", "type": "partner" }, "get_listed": { "id": "dd4a56e8-dd71-4d3b-861d-273e7283e726", "status": "pending", "company_name": "Tiger Woods Labs", "custom_form_response_id": "8f09e7fc-41ce-4ce0-b2a8-67300f66bda7", "directory_id": "827a617e-eb92-410d-b121-cf80e9023675", "directory_type": "generic_directory", "entry_type": "partner", "directory": { "directory_name": "Acme Partner Directory", "directory_owner_name": "Acme", "directory_owner_image": "https://cdn.partnerpage.io/media/contact_images/78d2bacb-7230-47d3-bce6-4b44df1b322d/logo.png" }, "user": { "id": "6b4f526b-027b-403e-9e8c-f686c9fa6772", "email": "founder@example.xyz", "first_name": "Jon", "last_name": "Smith" } }, "sent_by": { "email": "founder@example.xyz", "first_name": "Jon", "last_name": "Smith" } } }
profile_submitted_by_entry_owner
Field | Meaning |
| The partner profile submitted for review |
| Link to review the profile in your dashboard |
| The partner's own link to the profile |
| Directory id |
| Your organization: |
| The partner organization: |
| The partner user who submitted: |
{ "entry_id": "3d4969c6-8594-43b2-b076-a7ba336d4c96", "profile_url": "https://directory.partnerpage.io/directory/06c9b750-f5f5-4f7b-a199-6205f0397f63/entries/partner/edit/3d4969c6-8594-43b2-b076-a7ba336d4c96", "entry_owner_profile_url": "https://directory.partnerpage.io/profile-manager/profiles/solutions/partners/3d4969c6-8594-43b2-b076-a7ba336d4c96", "directory_id": "06c9b750-f5f5-4f7b-a199-6205f0397f63", "dir_owner": { "org_id": "0b7dee8c-dae5-41d4-b869-8dfdc0d26553", "org_name": "Acme", "directory_name": "Acme Partner Directory", "directory_owner_image": "https://cdn.partnerpage.io/media/contact_images/0b7dee8c-dae5-41d4-b869-8dfdc0d26553/logo.png" }, "entry_owner": { "org_id": "8b741711-bafd-4fd0-b535-30074a1f2dd6", "org_name": "ZZZ Consulting" }, "user": { "id": "be051b53-cc24-4b86-a790-0c04dc2d6d3d", "email": "jon@example.com", "first_name": "Jon", "last_name": "Smith" } }
Quick start with n8n, Zapier or Make
n8n. Add a Webhook node, set the method to
POSTand Respond to "Immediately". Activate the workflow and copy the Production URL into the Subscriber URL field.Zapier. Create a Zap with the trigger "Webhooks by Zapier", event "Catch Hook", and copy the URL it gives you.
Make. Add a "Custom webhook" module and copy its URL.
To test, open a partner profile in your directory and submit the contact form with your own details. Within a few seconds the delivery appears under Webhooks, then Logs, with the status your endpoint returned, and the event shows up in your flow.
Questions? Message us from the chat in your dashboard.
