# Directory webhook events and payloads

A directory can subscribe to nine events. This page lists when each one is sent and every field in its payload. To create a subscription, see [Set up webhooks for your directory](https://support.partnerpage.io/en/articles/17220171) in the Help Center. For headers, the signature check and how to reply, see [Verify and handle webhook deliveries](https://docs.partnerpage.io/verify-and-handle-webhook-deliveries-2467215m0).

## Events

The event name arrives in the `X-Event-Type` header.

| Event trigger | Sent when |
| --- | --- |
| `contact_request_created` | A new lead is created for a partner: a visitor submits the contact form on a partner profile, your team assigns a matchmaking request to a partner (one event per partner), or a lead is created through the API. |
| `contact_request_updated` | The lead's state changes (accepted, declined, closed or reopened) or its project status changes. One event per change. |
| `matchmaking_request_created` | A visitor submits your directory's matchmaking form ("help me find a partner"). |
| `matchmaking_request_updated` | Your team assigns the matchmaking request to one or more partners, sets or clears its owner, closes it or reopens it. |
| `partner_application_created` | Someone submits your partner application form. Requires partner applications to be enabled on the directory. Not sent for applications created through the API. |
| `partner_application_updated` | 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. |
| `partner_get_listed_completed` | A partner completes the self-service Get Listed flow, creating a listing that waits for your approval. |
| `profile_submitted_by_entry_owner` | A partner submits their profile, new or edited, for your review. |
| `public_profile_changed` | What visitors see on a partner's profile changes: partner edits you approve (or save, with approvals off), edits by your team in the dashboard or the API, or the partner being published, hidden or removed. |

## Conventions

- Ids are UUIDs, sent as strings.
- `created` timestamps are in UTC, formatted `YYYY-MM-DD HH:MM`.
- `custom_fields` lists the answers to the custom questions on your form. Each item has `export_label` (the label you gave the field), `internal_label` and `text_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 instead of rejecting the request.

## Lead object (`contact_request`)

Sent inside `contact_request_created` and `contact_request_updated`.

| Field | Meaning |
| --- | --- |
| `id` | Lead id |
| `created` | When the lead was created (UTC) |
| `state` | `pending`, `accepted`, `declined` or `closed` |
| `project_status` | `null`, `new`, `contacted`, `engaged`, `on_hold`, `secured`, `launched_deployed` or `lost` |
| `first_name`, `last_name`, `email`, `phone` | The lead's contact details |
| `company_name`, `company_website` | The lead's company |
| `comments` | The message the lead wrote |
| `terms`, `privacy`, `share` | Consent checkboxes on the form: `true`, `false` or `null` |
| `utm_query_params` | UTM parameters captured on the page, or `null` |
| `custom_form_response_id` | Id of the submitted form response |
| `source` | `contact_request` (submitted on a partner profile) or `matchmaking` (assigned from a matchmaking request) |
| `match_origin` | Id of the matchmaking request the lead came from, or `null` |
| `routing_source` | A free-text label set by the API integration that created the lead, for example the routing pool that picked the partner. `null` for leads created any other way |
| `entry` | The partner profile that received the lead: `id`, `title`, `owner` (the partner organization's id, the same Partner ID shown in the dashboard) and `is_deleted` (`true` when the profile has since been deleted) |
| `directory` | `id`, `name`, `org_id`, `org_name` and `contact_request_project_statuses_enabled` |
| `decline_reason`, `decline_reason_detail` | Set when the partner declined: `no_response_from_customer`, `not_a_fit`, `spam` or `other`, plus free text |
| `close_reason`, `close_reason_detail` | Set when the lead was closed: `partner_unresponsive`, `spam`, `duplicate`, `customer_withdrew` or `other`, plus free text |
| `legacy_filter_responses` | Filter answers from older directories, usually `null` |

## contact_request_created

| Field | Meaning |
| --- | --- |
| `entry_id` | Partner profile id |
| `directory_id` | Directory id |
| `contact_request` | The lead object above |
| `custom_fields` | Custom form answers |
| `directory_contact_request_url` | Link to the lead in your dashboard |

```json
{
  "directory_id": "dddddddd-0000-4000-8000-000000000001",
  "entry_id": "eeeeeeee-0000-4000-8000-000000000001",
  "contact_request": {
    "comments": "We need help rolling out the product to 200 users.",
    "company_name": "Example Manufacturing",
    "company_website": "https://www.example.com/",
    "created": "2025-11-05 16:19",
    "custom_form_response_id": "ffffffff-0000-4000-8000-000000000001",
    "decline_reason": null,
    "decline_reason_detail": null,
    "close_reason": null,
    "close_reason_detail": null,
    "directory": {
      "contact_request_project_statuses_enabled": true,
      "id": "dddddddd-0000-4000-8000-000000000001",
      "name": "Acme Partner Directory",
      "org_id": "aaaaaaaa-0000-4000-8000-000000000001",
      "org_name": "Acme"
    },
    "email": "jane.doe@example.com",
    "entry": {
      "id": "eeeeeeee-0000-4000-8000-000000000001",
      "owner": "aaaaaaaa-0000-4000-8000-000000000002",
      "title": "Northwind Consulting",
      "is_deleted": false
    },
    "first_name": "Jane",
    "id": "cccccccc-0000-4000-8000-000000000001",
    "last_name": "Doe",
    "legacy_filter_responses": null,
    "match_origin": null,
    "phone": "5555550100",
    "privacy": null,
    "project_status": null,
    "routing_source": null,
    "share": null,
    "source": "contact_request",
    "state": "pending",
    "terms": null,
    "utm_query_params": null
  },
  "custom_fields": [
    {
      "export_label": "Desired timeline",
      "internal_label": "desired_timeline",
      "text_value": "Q2"
    }
  ],
  "directory_contact_request_url": "https://directory.partnerpage.io/directories/dddddddd-0000-4000-8000-000000000001/contact-requests/cccccccc-0000-4000-8000-000000000001"
}
```

## 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 the partner accepts the lead:

```json
{
  "directory_id": "dddddddd-0000-4000-8000-000000000001",
  "entry_id": "eeeeeeee-0000-4000-8000-000000000001",
  "contact_request": {
    "comments": "We need help rolling out the product to 200 users.",
    "company_name": "Example Manufacturing",
    "company_website": "https://www.example.com/",
    "created": "2025-11-05 16:19",
    "custom_form_response_id": "ffffffff-0000-4000-8000-000000000001",
    "decline_reason": null,
    "decline_reason_detail": null,
    "close_reason": null,
    "close_reason_detail": null,
    "directory": {
      "contact_request_project_statuses_enabled": true,
      "id": "dddddddd-0000-4000-8000-000000000001",
      "name": "Acme Partner Directory",
      "org_id": "aaaaaaaa-0000-4000-8000-000000000001",
      "org_name": "Acme"
    },
    "email": "jane.doe@example.com",
    "entry": {
      "id": "eeeeeeee-0000-4000-8000-000000000001",
      "owner": "aaaaaaaa-0000-4000-8000-000000000002",
      "title": "Northwind Consulting",
      "is_deleted": false
    },
    "first_name": "Jane",
    "id": "cccccccc-0000-4000-8000-000000000001",
    "last_name": "Doe",
    "legacy_filter_responses": null,
    "match_origin": null,
    "phone": "5555550100",
    "privacy": null,
    "project_status": null,
    "routing_source": null,
    "share": null,
    "source": "contact_request",
    "state": "accepted",
    "terms": null,
    "utm_query_params": null
  },
  "custom_fields": [
    {
      "export_label": "Desired timeline",
      "internal_label": "desired_timeline",
      "text_value": "Q2"
    }
  ],
  "directory_contact_request_url": "https://directory.partnerpage.io/directories/dddddddd-0000-4000-8000-000000000001/contact-requests/cccccccc-0000-4000-8000-000000000001"
}
```

When the partner declines, `state` is `declined` and `decline_reason` (with `decline_reason_detail`) is set. When your team closes the lead, `state` is `closed` and `close_reason` (with `close_reason_detail`) is set.

## matchmaking_request_created

| Field | Meaning |
| --- | --- |
| `request_id` | Matchmaking request id |
| `directory_id`, `directory_name`, `directory_owner_name` | Your directory and organization |
| `custom_fields` | Custom form answers |
| `request_details` | The request itself, described below |

Fields of `request_details`:

| Field | Meaning |
| --- | --- |
| `id`, `created` | Request id and when it was submitted (UTC) |
| `state` | `new`, `assigned` or `closed` |
| `first_name`, `last_name`, `email`, `phone` | The requester's contact details |
| `company_name`, `company_website` | The requester's company |
| `comments` | The message the requester wrote |
| `terms`, `privacy`, `share` | Consent checkboxes on the form |
| `custom_legal_text`, `custom_legal_text_value` | Your custom legal checkbox and its answer, or `null` |
| `assigned_partners` | The partners the request was assigned to: a list of `partner_entry__id` and `partner_name` |
| `date_assigned` | When it was last assigned (ISO 8601), or `null` |
| `owner`, `owner_name` | The team member who owns the request, or `null` |
| `close_reason`, `close_reason_detail` | Set when the request was closed: `no_suitable_partner`, `spam`, `duplicate`, `customer_withdrew` or `other`, plus free text |
| `utm_query_params` | UTM parameters captured on the page, or `null` |
| `custom_form_response_id` | Id of the submitted form response, or `null` |
| `directory` | `id` and `name` |
| `legacy_filter_responses` | Filter answers from older directories, usually `null` |

```json
{
  "request_id": "cccccccc-0000-4000-8000-000000000002",
  "directory_id": "dddddddd-0000-4000-8000-000000000001",
  "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": "cccccccc-0000-4000-8000-000000000002",
    "created": "2025-11-05 16:19",
    "state": "new",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane.doe@example.com",
    "phone": "5555550100",
    "company_name": "Example Manufacturing",
    "company_website": "https://www.example.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": null,
    "directory": {
      "id": "dddddddd-0000-4000-8000-000000000001",
      "name": "Acme"
    },
    "legacy_filter_responses": null,
    "close_reason": null,
    "close_reason_detail": null
  }
}
```

## matchmaking_request_updated

Same fields as `matchmaking_request_created`. For example, after your team assigns the request to two partners:

```json
{
  "request_id": "cccccccc-0000-4000-8000-000000000002",
  "directory_id": "dddddddd-0000-4000-8000-000000000001",
  "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": "cccccccc-0000-4000-8000-000000000002",
    "created": "2025-11-05 16:19",
    "state": "assigned",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane.doe@example.com",
    "phone": "5555550100",
    "company_name": "Example Manufacturing",
    "company_website": "https://www.example.com/",
    "terms": true,
    "privacy": true,
    "share": false,
    "comments": "Looking for an implementation partner",
    "assigned_partners": [
      {
        "partner_entry__id": "eeeeeeee-0000-4000-8000-000000000001",
        "partner_name": "Northwind Consulting"
      },
      {
        "partner_entry__id": "eeeeeeee-0000-4000-8000-000000000002",
        "partner_name": "Bluebird Labs"
      }
    ],
    "date_assigned": "2025-11-05T16:25:03.120453+00:00",
    "custom_legal_text": null,
    "custom_legal_text_value": null,
    "owner": "bbbbbbbb-0000-4000-8000-000000000003",
    "owner_name": "Chris Park",
    "utm_query_params": null,
    "custom_form_response_id": null,
    "directory": {
      "id": "dddddddd-0000-4000-8000-000000000001",
      "name": "Acme"
    },
    "legacy_filter_responses": null,
    "close_reason": null,
    "close_reason_detail": null
  }
}
```

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.

When your team closes the request, `state` becomes `closed` and `close_reason` is set.

## partner_application_created

| Field | Meaning |
| --- | --- |
| `application_id` | Application id |
| `directory_id`, `directory_name`, `directory_owner_name` | Your directory and organization |
| `custom_fields` | Custom form answers |
| `application_details` | The application: `id`, `created`, `state` (`pending`, `accepted` or `declined`), `first_name`, `last_name`, `email`, `company_name`, `company_website`, `custom_form_response_id` and `directory` (`id`, `org_name`) |

```json
{
  "application_id": "cccccccc-0000-4000-8000-000000000003",
  "directory_id": "dddddddd-0000-4000-8000-000000000001",
  "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": "cccccccc-0000-4000-8000-000000000003",
    "created": "2025-11-05 16:19",
    "state": "pending",
    "first_name": "Sam",
    "last_name": "Lee",
    "email": "founder@example.com",
    "company_name": "Bluebird Labs",
    "company_website": "https://www.example.com/",
    "custom_form_response_id": "ffffffff-0000-4000-8000-000000000001",
    "directory": {
      "id": "dddddddd-0000-4000-8000-000000000001",
      "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`. For example, after an application is accepted:

```json
{
  "application_id": "cccccccc-0000-4000-8000-000000000003",
  "entry_id": "eeeeeeee-0000-4000-8000-000000000002",
  "entry_title": "Bluebird Labs",
  "directory_id": "dddddddd-0000-4000-8000-000000000001",
  "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": "cccccccc-0000-4000-8000-000000000003",
    "created": "2025-11-05 16:19",
    "state": "accepted",
    "first_name": "Sam",
    "last_name": "Lee",
    "email": "founder@example.com",
    "company_name": "Bluebird Labs",
    "company_website": "https://www.example.com/",
    "custom_form_response_id": "ffffffff-0000-4000-8000-000000000001",
    "directory": {
      "id": "dddddddd-0000-4000-8000-000000000001",
      "org_name": "Acme"
    }
  }
}
```

## partner_get_listed_completed

| Field | Meaning |
| --- | --- |
| `customer_id` | Email of the partner user who completed Get Listed |
| `event_name` | Always `partner_get_listed_completed` |
| `data.entry` | The listing created: `id`, `title` and `type` |
| `data.get_listed` | The Get Listed record: `id`, `status`, `company_name`, `directory_id`, `directory_type`, `entry_type`, `custom_form_response_id`, `profile_url` (link to review the listing in your dashboard), `directory` (`directory_name`, `directory_owner_name`, `directory_owner_image`) and `user` (`id`, `email`, `first_name`, `last_name`) |
| `data.sent_by` | Who submitted it: `email`, `first_name`, `last_name` |

```json
{
  "customer_id": "founder@example.com",
  "event_name": "partner_get_listed_completed",
  "data": {
    "entry": {
      "id": "eeeeeeee-0000-4000-8000-000000000002",
      "title": "Bluebird Labs",
      "type": "partner"
    },
    "get_listed": {
      "company_name": "Bluebird Labs",
      "custom_form_response_id": "ffffffff-0000-4000-8000-000000000002",
      "directory": {
        "directory_name": "Acme Partner Directory",
        "directory_owner_image": "https://cdn.partnerpage.io/media/contact_images/aaaaaaaa-0000-4000-8000-000000000001/logo.png",
        "directory_owner_name": "Acme"
      },
      "directory_id": "dddddddd-0000-4000-8000-000000000001",
      "directory_type": "generic_directory",
      "entry_type": "partner",
      "id": "cccccccc-0000-4000-8000-000000000004",
      "profile_url": "https://directory.partnerpage.io/directories/dddddddd-0000-4000-8000-000000000001/directory/partners/edit/eeeeeeee-0000-4000-8000-000000000002",
      "status": "pending",
      "user": {
        "email": "founder@example.com",
        "first_name": "Sam",
        "id": "bbbbbbbb-0000-4000-8000-000000000002",
        "last_name": "Lee"
      }
    },
    "sent_by": {
      "email": "founder@example.com",
      "first_name": "Sam",
      "last_name": "Lee"
    }
  }
}
```

## profile_submitted_by_entry_owner

| Field | Meaning |
| --- | --- |
| `entry_id` | The partner profile submitted for review |
| `profile_url` | Link to review the profile in your dashboard |
| `entry_owner_profile_url` | The partner's own link to the profile |
| `directory_id` | Directory id |
| `dir_owner` | Your organization: `org_id`, `org_name`, `directory_name`, `directory_owner_image` |
| `entry_owner` | The partner organization: `org_id`, `org_name` |
| `user` | The partner user who submitted: `id`, `email`, `first_name`, `last_name` |

```json
{
  "dir_owner": {
    "directory_name": "Acme Partner Directory",
    "directory_owner_image": "https://cdn.partnerpage.io/media/contact_images/aaaaaaaa-0000-4000-8000-000000000001/logo.png",
    "org_id": "aaaaaaaa-0000-4000-8000-000000000001",
    "org_name": "Acme"
  },
  "directory_id": "dddddddd-0000-4000-8000-000000000001",
  "entry_id": "eeeeeeee-0000-4000-8000-000000000001",
  "entry_owner": {
    "org_id": "aaaaaaaa-0000-4000-8000-000000000002",
    "org_name": "Northwind Consulting"
  },
  "entry_owner_profile_url": "https://directory.partnerpage.io/profiles/solutions/dddddddd-0000-4000-8000-000000000001/partner/eeeeeeee-0000-4000-8000-000000000001",
  "profile_url": "https://directory.partnerpage.io/directories/dddddddd-0000-4000-8000-000000000001/directory/partners/edit/eeeeeeee-0000-4000-8000-000000000001",
  "user": {
    "email": "chris.park@example.com",
    "first_name": "Chris",
    "id": "bbbbbbbb-0000-4000-8000-000000000001",
    "last_name": "Park"
  }
}
```

## public_profile_changed

Sent when what visitors see on a partner's profile changes:

- a partner's edits, once you approve them (approvals on) or as soon as the partner saves (approvals off)
- edits your team makes in the dashboard or through the API, including bulk partner updates (one delivery per partner that changed)
- a partner's options on filters shown in your directory's filter bar or on profiles
- a partner becoming visible (public or searchable), being hidden or made inactive, or being removed

Not sent for draft edits, submissions for review, rejections or discarded drafts, for fields visitors never see (internal directory fields, External ID), for edits to a hidden partner, or for saves that change nothing visible. Saves of the same partner a few seconds apart produce one delivery, a few seconds after the save. When you subscribe, PartnerPage records how every partner looks right then, so deliveries start with the next real change.

The payload tells you which partner changed, not what changed. Read the profile again through the public partner read, `GET /api/directory/public/v1alpha/directories/{directory_id}/entries/partners/slugs/{entry_slug}/`, or remove it from your copy when `change` is `unpublished`.

| Field | Meaning |
| --- | --- |
| `change` | `updated` (the visible profile changed), `published` (became visible), `unpublished` (hidden, made inactive or removed) |
| `directory_id` | Your directory |
| `entry_id` | The partner |
| `entry_slug` | The partner's public slug, for the public partner read; `null` once the partner is removed |
| `entry_title` | The partner's name |
| `entry_status` | `public`, `searchable`, `hidden` or `inactive` |
| `entry_deleted` | `true` when the partner was removed |
| `changed_at` | When PartnerPage detected the change (ISO 8601, UTC) |

```json
{
  "change": "updated",
  "directory_id": "dddddddd-0000-4000-8000-000000000001",
  "entry_id": "eeeeeeee-0000-4000-8000-000000000001",
  "entry_slug": "northwind-consulting",
  "entry_title": "Northwind Consulting",
  "entry_status": "public",
  "entry_deleted": false,
  "changed_at": "2026-10-15T16:19:00.512000+00:00"
}
```

