Projects (REST API)

Create projects from your system, look up leads, list projects, fetch team members, and send a test webhook. You may instead keep project creation in Catapult; webhooks still fire when a webhook URL is configured on your Public API credentials. Correlate on projectID. external_project_id is your partner id from POST /createProject, or null when the project was created in Catapult. Webhook payloads always include projectName. GET project_id accepts the partner Extended ID or Catapult projectID. Webhook payloads are in the Webhooks section below.

Operations

Method Path Summary
POST /oauth/token Request an access token with the client id and secret Catapult issued to your company
POST /createProject Create project
GET /getPotentialProjectLeads Get project leads
GET /getProjectList Get project list with IDs
GET /getAllTeamMembers Get all active team members in your business
GET /testWebhook Send dummy webhook events to your endpoint
Authentication - When OAuth is enabled for your company, send Authorization: Bearer and Ocp-Apim-Subscription-Key. The token is checked first. The subscription key selects your company. See Authentication.

Authentication

Catapult issues an access token for your company. Use the client id and client secret Catapult shared with you. Request the token from the same environment you call.

Environment Token URL
Migration https://public-api-integration.azure-api.net/migration-public-api/oauth/token
Production https://public-api-integration.azure-api.net/production-public-api/oauth/token
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
client_id=YOUR_CATAPULT_CLIENT_ID
client_secret=YOUR_CATAPULT_CLIENT_SECRET

A successful response includes access_token, token_type (Bearer), and expires_in. Request a new token when it expires. Send the client secret only to the token URL.

Then call any operation with both headers. Catapult checks the access token, then continues with the subscription key checks that select your company.

Header Required when OAuth is enabled Description
Authorization Yes Bearer <access_token> from POST /oauth/token.
Ocp-Apim-Subscription-Key Yes Your primary or secondary subscription key. Selects your company.

If OAuth is not enabled for your company, send the subscription key only. The operations, request bodies, and webhook event payloads stay the same.

Environments

Use the Migration base URL while integrating, and the Production base URL for live traffic. Code samples below show both URLs for every operation.

Environment Base URL
Migration https://public-api-integration.azure-api.net/migration-public-api
Production https://public-api-integration.azure-api.net/production-public-api
POST /createProject

Create project

Creates a new RFP project in Catapult and registers your external external_project_id

Recommended flow - Call Get project leads first and use returned email values in projectLeadEmail.

Request

Header Required Description
Authorization Yes Bearer <access_token> from POST /oauth/token when OAuth is enabled for your company. Checked before the subscription key.
Ocp-Apim-Subscription-Key Yes Primary or secondary APIM subscription key. Resolves your business_id via public_api_business_credentials.
Content-Type Yes Must be application/json.
Field Type Required Description
projectName string Yes Display name (max 100 chars). Allowed: letters, digits, space, - & , ( ) . ' $ +.
projectLeadEmail string or string[] Yes Comma-separated string or JSON array. Must match active users in your business.
external_project_id string Yes Your external project id (stored in registry).

business_id is not accepted from clients in production, it is resolved from your subscription key.

Responses

200 OK - Create Project Success

Property Type Description
flag string "success"
message string Human-readable confirmation
external_project_id string Your external id (echo)
projectID number Catapult id (stable Catapult identifier)
status string e.g. "Draft"
Example
{
    "flag": "success",
    "message": "Project created successfully",
    "external_project_id": "EXT-PROJECT-10042",
    "projectID": 12345,
    "status": "Draft"
  }
  • 400 Bad Request - missing/invalid fields, invalid projectName, or no matching users for projectLeadEmail.
  • 401 Unauthorized - missing bearer token or subscription key.
  • 403 Forbidden - invalid or inactive subscription.

Code samples

# Migration
curl --request POST \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/createProject' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --data '{
      "projectName": "2026 Annual RFP",
      "projectLeadEmail": "jane.doe@example.com",
      "external_project_id": "EXT-PROJECT-10042"
    }'

# Production
curl --request POST \
    --url 'https://public-api-integration.azure-api.net/production-public-api/createProject' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --data '{
      "projectName": "2026 Annual RFP",
      "projectLeadEmail": "jane.doe@example.com",
      "external_project_id": "EXT-PROJECT-10042"
    }'

GET /getPotentialProjectLeads

Get project leads

Returns active users in your Catapult business who may be assigned as project leads. Excludes roles: Corporate Contact, Auditor, Committee Member.

Request

Header Required Description
Authorization Yes Bearer <access_token> from POST /oauth/token when OAuth is enabled for your company. Checked before the subscription key.
Ocp-Apim-Subscription-Key Yes Primary or secondary APIM subscription key. Resolves your business_id via public_api_business_credentials.
Accept No Recommended: application/json.

No query or path parameters. No request body.

Responses

200 OK - Get ProjectLead Success

Property Type Description
flag string "success"
message string Human-readable confirmation
projectLead array List of eligible users
Property Type Description
full_name string User display name
email string Decrypted email address
role string Catapult role
department_name string Department name, if set
Example
{
    "flag": "success",
    "message": "Project lead fetched successfully",
    "projectLead": [
      {
        "full_name": "Jane Doe",
        "email": "jane.doe@example.com",
        "role": "Admin",
        "department_name": "Underwriting"
      }
    ]
  }
  • 401 Unauthorized - same as Create project.
  • 403 Forbidden - same as Create project.

Code samples

# Migration
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/getPotentialProjectLeads' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --header 'Accept: application/json'

# Production
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/production-public-api/getPotentialProjectLeads' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --header 'Accept: application/json'

GET /getProjectList

Get project list with IDs

Returns active projects in your Catapult business: Catapult projectID, partner external_project_id (null if the project was created in Catapult and no partner id is stored), name, dates, and active invitee count.

Request

Header Required Description
Authorization Yes Bearer <access_token> from POST /oauth/token when OAuth is enabled for your company. Checked before the subscription key.
Ocp-Apim-Subscription-Key Yes Primary or secondary APIM subscription key. Resolves your business_id via public_api_business_credentials.
Accept No Recommended: application/json.

No query or path parameters. No request body.

Responses

200 OK - Get Project List Success

Property Type Description
flag string "success"
message string Human-readable confirmation
projects array List of active projects
Property Type Description
projectID number Catapult projects.id
external_project_id string | null Partner Extended ID from POST /createProject; null when the project was created in Catapult
projectName string | null Project display name
acceptBy string | null Accept-by date (MM/DD/YYYY)
questionSubmissionDate string | null Question submission date (MM/DD/YYYY)
dueDate string | null Due date (MM/DD/YYYY)
totalInviteeCount number Active invitees
Example
{
    "flag": "success",
    "message": "Project list fetched successfully",
    "projects": [
      {
        "projectID": 12345,
        "external_project_id": "EXT-PROJECT-10042",
        "projectName": "2026 Annual RFP",
        "acceptBy": "03/15/2026",
        "questionSubmissionDate": "04/01/2026",
        "dueDate": "04/30/2026",
        "totalInviteeCount": 12
      },
      {
        "projectID": 12346,
        "external_project_id": null,
        "projectName": "Internal Q2 Review",
        "acceptBy": "05/01/2026",
        "questionSubmissionDate": null,
        "dueDate": "05/30/2026",
        "totalInviteeCount": 3
      }
    ]
  }
  • 401 Unauthorized - same as Create project.
  • 403 Forbidden - same as Create project.

Code samples

# Migration
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/getProjectList' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --header 'Accept: application/json'

# Production
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/production-public-api/getProjectList' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --header 'Accept: application/json'

GET /getAllTeamMembers

Get all team members

Returns every active, non-deleted user in your Catapult business. Results are ordered by first name, then user id. Email addresses are decrypted. date_created is the registration time in UTC (YYYY-MM-DDTHH:mm:ssZ). An empty team returns 200 with teamMembers: [] and totalCount: 0.

Request

Header Required Description
Authorization Yes Bearer <access_token> from POST /oauth/token when OAuth is enabled for your company. Checked before the subscription key.
Ocp-Apim-Subscription-Key Yes Primary or secondary APIM subscription key. Resolves your business_id via public_api_business_credentials.
Accept No Recommended: application/json.

No query or path parameters. No request body.

Responses

200 OK - Get All Team Members Success

Property Type Description
flag string "success"
message string Human-readable confirmation
totalCount number Number of team members returned
teamMembers array List of team members
Property Type Description
id number Catapult users.id
full_name string | null Full display name
first_name string | null First name
last_name string | null Last name
email string | null Decrypted email address
role string Catapult role; Admin users are returned as "Admin"
title string | null Job title
phone string | null Phone number
department_name string | null Department name
date_created string | null Registration time in UTC (YYYY-MM-DDTHH:mm:ssZ)
Example
{
    "flag": "success",
    "message": "Team members fetched successfully",
    "totalCount": 1,
    "teamMembers": [
      {
        "id": 101,
        "full_name": "Jane Doe",
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane.doe@example.com",
        "role": "Admin",
        "title": "Underwriter",
        "phone": "5551234567",
        "department_name": "Underwriting",
        "date_created": "2022-02-07T06:00:29Z"
      }
    ]
  }
  • 401 Unauthorized - missing or invalid bearer token, or missing subscription key.
  • 403 Forbidden - invalid or inactive subscription key.

Code samples

# Migration
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/getAllTeamMembers' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --header 'Accept: application/json'

# Production
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/production-public-api/getAllTeamMembers' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --header 'Accept: application/json'

GET /testWebhook

Test webhook

Sends dummy webhook envelopes to the webhook URL stored on your Public API credentials. Nothing in Catapult is updated. Use this to confirm your endpoint is reachable and that verification headers are present.

Dummy payloads use projectID 0, external_project_id "TEST-WEBHOOK", projectName "Test Project", and correlationId "test-webhook". Supplier dummies include supplier_id, domain, and businessName (or null when the company name is unknown). The Scoring Status dummy includes a complete scorers list with email, fullName, and status. Finalist and winner dummies include supplier_id "supplier-0", domain "example.com", and businessName. Ignore these in production processing.

Request

Header Required Description
Authorization Yes Bearer <access_token> from POST /oauth/token when OAuth is enabled for your company. Checked before the subscription key.
Ocp-Apim-Subscription-Key Yes Primary or secondary APIM subscription key. Resolves your business_id via public_api_business_credentials.

No path parameters. Omit eventType to send all event types.

Field Type Required Description
eventType string No One webhook event type. Omit to send all.

Allowed eventType values:

  • project.dates.updated
  • project.status.updated
  • project.suppliers.updated
  • project.scoring.status_updated
  • project.finalists.selection_changed
  • project.winners.selection_changed

Responses

200 OK

Returns a deliveries array with eventType, eventId, ok, and the HTTP statusCode from your webhook endpoint.

Example
{
    "flag": "success",
    "message": "Test webhook events sent. These are dummy events and are not tied to a real project update.",
    "webhookUrl": "https://example.com/webhooks/catapult",
    "deliveries": [
      {
        "eventType": "project.status.updated",
        "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "ok": true,
        "statusCode": 200
      }
    ]
  }
  • 400 Bad Request - webhook URL missing/invalid, or unknown eventType.
  • 401 Unauthorized - same as Create project.
  • 403 Forbidden - same as Create project.

Code samples

# Migration - all event types
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/testWebhook' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY'

# Production - all event types
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/production-public-api/testWebhook' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY'

# Migration - Scoring Status only
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/testWebhook?eventType=project.scoring.status_updated' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY'

# Production - Scoring Status only
curl --request GET \
    --url 'https://public-api-integration.azure-api.net/production-public-api/testWebhook?eventType=project.scoring.status_updated' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY'

Webhook payload reference

Catapult sends JSON webhooks to your configured endpoint when project dates, status, participants, finalists, or winners change. Each message uses a shared envelope; event-specific fields live under payload.

When webhooks fire: Catapult posts to your configured webhook URL for any business with active Public API credentials and a non-empty webhook URL. There is no business-id allowlist. Projects created in Catapult (not via POST /createProject) still emit events. Those payloads set external_project_id to null and include projectID (Catapult projects.id) plus payload.projectName. Correlate on projectID when there is no partner id. GET operations that take project_id accept that partner id or Catapult projectID. Use GET /testWebhook to send dummy events without changing a project.

REST API operations are in the Projects (REST API) section above.

Shared envelope

Every webhook includes these top-level fields (in addition to payload):

Field Type Description
schemaVersion number Payload schema version (currently 1).
eventId string (UUID) Unique id for this delivery.
eventType string Event discriminator (see sections below).
occurredAt string (ISO 8601) UTC timestamp when the change occurred.
source string Always "catapulthq" for production events.
correlationId string | null Optional trace id; often null on live events. Test deliveries use "test-webhook".
payload object Event-specific body documented per section.
Date format: Fields such as acceptBy use MM/DD/YYYY when set, or null when cleared or not applicable.

Verification

Catapult POSTs JSON to your webhook URL (Content-Type: application/json). Return a 2xx status; non-2xx responses are retried.

When your company has given Catapult an OAuth URL, client id, and client secret, each webhook POST includes Authorization: Bearer. Catapult requests that token from your OAuth URL. The event body is not HMAC-signed. x-webhook-secret is sent only when that shared secret is also configured.

Item Value
Header Authorization: Bearer <access_token>
Scheme Access token Catapult requested from the OAuth URL, client id, and client secret you provided.
Optional header x-webhook-secret when a shared secret is also configured.
Body Raw JSON envelope. Not HMAC-signed.

Use GET /testWebhook to send dummy envelopes to your configured URL without updating a project.

1. Project date updates

eventType: project.dates.updated

Sent when a project is created in Catapult with any of these dates set, and whenever project acceptance, question submission, or due dates change afterwards. The project.status.updated event carries no dates, so this is the event to read dates from. A date that is not set is null.

Payload fields

Field Type Description
external_project_id string | null Partner Extended ID from Public API create, or null when the project was created in Catapult. Correlate on projectID when this field is null.
projectName string | null Catapult project display name. Always included (or null if the project has no name).
projectID number Catapult id - stable Catapult project identifier.
acceptBy string | null Deadline for project acceptance (MM/DD/YYYY).
questionSubmissionDate string | null Cutoff for submitting questions (MM/DD/YYYY).
dueDate string | null Final project due date (MM/DD/YYYY).
Example
{
    "schemaVersion": 1,
    "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "eventType": "project.dates.updated",
    "occurredAt": "2026-05-19T16:00:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "acceptBy": "06/15/2026",
      "questionSubmissionDate": "06/01/2026",
      "dueDate": "06/30/2026"
    }
  }

Example - Catapult-created project (no partner id)

Example
{
    "schemaVersion": 1,
    "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "eventType": "project.dates.updated",
    "occurredAt": "2026-05-19T16:00:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": null,
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "acceptBy": "06/15/2026",
      "questionSubmissionDate": "06/01/2026",
      "dueDate": "06/30/2026"
    }
  }

Example - date cleared

Example
{
    "schemaVersion": 1,
    "eventId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "eventType": "project.dates.updated",
    "occurredAt": "2026-05-19T16:05:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "acceptBy": "06/15/2026",
      "questionSubmissionDate": null,
      "dueDate": "06/30/2026"
    }
  }

2. Project status updates

eventType: project.status.updated

Sent only when the human-readable project lifecycle status actually changes. Repeat activity that leaves the status untouched - such as inviting a second or third supplier once the project is already Invited - does not send an event. This event can also carry a webhook-only Scoring Complete value when every scorer has marked scoring complete. That value is not stored as a project lifecycle status in Catapult.

Payload fields

Field Type Required Description
external_project_id string | null Yes Partner Extended ID from Public API create, or null when the project was created in Catapult. Correlate on projectID when this field is null.
projectName string | null No Catapult project display name. Always included (or null if the project has no name).
projectID number Yes Catapult id - stable Catapult project identifier.
projectStatus string Yes Human-readable lifecycle status, or webhook-only Scoring Complete (see values below).

Project Status Values

Value Typical trigger
DraftNew project created, review round reset to draft.
InvitedFirst participant invited. Later invitees do not change the project status, so no event is sent for them - use project.suppliers.updated to track each invitee.
Pending ApprovalProject sent for review round.
ApprovedRequestor review approved.
CompletedProject closed or winner selected.
Scoring CompleteWebhook-only. Sent when every scorer has marked themselves Complete (shared collaborative status, or every assigned individual scorer). This does not change the project's lifecycle status in Catapult; per-scorer detail still arrives on project.scoring.status_updated.
Example
{
    "schemaVersion": 1,
    "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "eventType": "project.status.updated",
    "occurredAt": "2026-05-19T14:30:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "projectStatus": "Invited"
    }
  }
Webhook-only scoring complete
{
    "schemaVersion": 1,
    "eventId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "eventType": "project.status.updated",
    "occurredAt": "2026-05-19T15:45:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "projectStatus": "Scoring Complete"
    }
  }

3. Invitee information

Use project.suppliers.updated as a live snapshot of the Invite Responders table. It fires whenever a supplier is added, an invite is rescinded, or any invitee status changes (SendGrid email events or Catapult outcomes). Replace your local table with invitees on every event. Use supplier_id to uniquely identify an invitee when multiple people share the same email domain.

eventType: project.suppliers.updated

This event replaces the previous project.participants.invited, project.participants.invite_rescinded, and project.participants.status_updated events.

Payload fields

Field Type Description
external_project_id string | null Partner Extended ID from Public API create, or null when the project was created in Catapult. Correlate on projectID when this field is null.
projectName string | null Catapult project display name. Always included (or null if the project has no name).
projectID number Catapult id - stable Catapult project identifier.
totalInviteeCount number Count of all active invitees on the project. Always the full table, never a changed subset.
inviteeEmailDomains string[] Unique domains from every active invitee, lowercased, sorted (e.g. ["acme.com","broker.com"]).
invitees object[] Full Invite Responders table. Every active invitee is included on every event, with supplier_id, domain, businessName, and display status.
invitees[].supplier_id string Stable id for the invitee: supplier-{{questionnaire_participants.id}}. Use this when two invitees share a domain.
invitees[].domain string | null Email domain for the invitee (lowercased).
invitees[].businessName string | null Responder company name when known (business name, else user company). null when it cannot be resolved.
invitees[].status string Single latest status. Catapult outcome wins when set (Undecided, Accepted, Declined, Completed, Winner); otherwise the latest SendGrid status (Status Pending, Email Delivered, Email Dropped, Email Deferred, Email Bounced, Email Opened, Email Clicked, Spam Report, Unsubscribe).

Example

Fired for add, rescind, SendGrid email events (delivered, dropped, deferred, bounce, open, click, spamreport, unsubscribe), and Catapult responder status changes. Replace the table by projectID. status is always the single latest value (Catapult wins over SendGrid). A rescinded supplier is omitted from invitees.

Example
{
    "schemaVersion": 1,
    "eventId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "eventType": "project.suppliers.updated",
    "occurredAt": "2026-05-19T15:00:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "totalInviteeCount": 3,
      "inviteeEmailDomains": ["acme.com", "contoso.com", "example.org"],
      "invitees": [
        { "supplier_id": "supplier-101", "domain": "acme.com", "businessName": "Acme Insurance", "status": "Email Opened" },
        { "supplier_id": "supplier-102", "domain": "contoso.com", "businessName": "Contoso Ltd", "status": "Accepted" },
        { "supplier_id": "supplier-103", "domain": "example.org", "businessName": null, "status": "Status Pending" }
      ]
    }
  }

4. Scoring Status

eventType: project.scoring.status_updated

A full snapshot of the people currently responsible for scoring. It is sent when the first responder submits, a scorer is added or removed, a score is first entered, a status is manually changed, or the project switches between collaborative and individual scoring. Replace your local scorer-status list with scorers on every event.

If no scorers are assigned, the list contains the project leads. The first assigned scorer replaces that fallback list. Collaborative scoring gives every scorer the same status; individual scoring can give each scorer a different status. Removing a score does not move an In Progress status back to Not Started. When every scorer has marked Complete, Catapult also sends project.status.updated with webhook-only projectStatus Scoring Complete.

Payload fields

FieldTypeDescription
external_project_idstring | nullPartner Extended ID, or null when none is registered.
projectNamestring | nullCatapult project display name.
projectIDnumberStable Catapult project identifier.
scoringTypestringCollaborative or Individual.
scorersobject[]Complete current scorer list, or project-lead fallback list.
scorers[].emailstring | nullScorer email address.
scorers[].fullNamestring | nullScorer full name (first and last name).
scorers[].statusstringNot Started, In Progress, or Completed.

Example

Individual scoring snapshot
{
    "schemaVersion": 1,
    "eventId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "eventType": "project.scoring.status_updated",
    "occurredAt": "2026-05-19T15:30:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "scoringType": "Individual",
      "scorers": [
        { "email": "alex.scorer@example.com", "fullName": "Alex Morgan", "status": "Completed" },
        { "email": "jordan.scorer@example.com", "fullName": "Jordan Blake", "status": "In Progress" },
        { "email": "taylor.scorer@example.com", "fullName": "Taylor Reed", "status": "Not Started" }
      ]
    }
  }

5. Finalist updates

eventType: project.finalists.selection_changed

Sent when finalists are selected or cleared. Company names stay in finalistCompanyNames. Each selected participant is also listed in finalists with supplier_id, domain, and businessName (correlate with invitees[]). Both arrays are empty when all finalists are cleared.

Payload fields

Field Type Description
external_project_id string | null Partner Extended ID from Public API create, or null when the project was created in Catapult. Correlate on projectID when this field is null.
projectName string | null Catapult project display name. Always included (or null if the project has no name).
projectID number Catapult id - stable Catapult project identifier.
finalistCompanyNames string[] Distinct company names for active participants with finalist recipient.
finalists object[] Active finalist participants: supplier_id, domain, and businessName.
finalists[].supplier_id string Stable id: supplier-{{questionnaire_participants.id}}.
finalists[].domain string | null Email domain for the finalist (lowercased).
finalists[].businessName string | null Responder business name (falls back to user company if business name is empty).
Example
{
    "schemaVersion": 1,
    "eventId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "eventType": "project.finalists.selection_changed",
    "occurredAt": "2026-05-19T16:00:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "finalistCompanyNames": ["Acme Insurance", "Beta Brokers LLC"],
      "finalists": [
        { "supplier_id": "supplier-101", "domain": "acme.com", "businessName": "Acme Insurance" },
        { "supplier_id": "supplier-102", "domain": "betabrokers.com", "businessName": "Beta Brokers LLC" }
      ]
    }
  }

Example - Catapult-created project (no partner id)

Example
{
    "schemaVersion": 1,
    "eventId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "eventType": "project.finalists.selection_changed",
    "occurredAt": "2026-05-19T16:00:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": null,
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "finalistCompanyNames": ["Acme Insurance", "Beta Brokers LLC"],
      "finalists": [
        { "supplier_id": "supplier-101", "domain": "acme.com", "businessName": "Acme Insurance" },
        { "supplier_id": "supplier-102", "domain": "betabrokers.com", "businessName": "Beta Brokers LLC" }
      ]
    }
  }

6. Winner updates

eventType: project.winners.selection_changed

Sent when winners are selected. Company names stay in winnerCompanyNames. Each winner is also listed in winners with supplier_id, domain, and businessName (correlate with invitees[]).

Payload fields

Field Type Description
external_project_id string | null Partner Extended ID from Public API create, or null when the project was created in Catapult. Correlate on projectID when this field is null.
projectName string | null Catapult project display name. Always included (or null if the project has no name).
projectID number Catapult id - stable Catapult project identifier.
winnerCompanyNames string[] Distinct company names for active participants with (winner). Same name resolution as finalists.
winners object[] Active winner participants: supplier_id, domain, and businessName.
winners[].supplier_id string Stable id: supplier-{{questionnaire_participants.id}}.
winners[].domain string | null Email domain for the winner (lowercased).
winners[].businessName string | null Responder business name (falls back to user company if business name is empty).
Example
{
    "schemaVersion": 1,
    "eventId": "d4e5f6a7-b8c9-0123-def0-234567890123",
    "eventType": "project.winners.selection_changed",
    "occurredAt": "2026-05-19T17:00:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "winnerCompanyNames": ["Acme Insurance"],
      "winners": [
        { "supplier_id": "supplier-101", "domain": "acme.com", "businessName": "Acme Insurance" }
      ]
    }
  }

Example - Catapult-created project (no partner id)

Example
{
    "schemaVersion": 1,
    "eventId": "d4e5f6a7-b8c9-0123-def0-234567890123",
    "eventType": "project.winners.selection_changed",
    "occurredAt": "2026-05-19T17:00:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": null,
      "projectName": "2026 Annual RFP",
      "projectID": 12345,
      "winnerCompanyNames": ["Acme Insurance"],
      "winners": [
        { "supplier_id": "supplier-101", "domain": "acme.com", "businessName": "Acme Insurance" }
      ]
    }
  }