Projects (REST API)

Create projects from your system, look up leads, list projects, 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 /createProject Create project
GET /getPotentialProjectLeads Get project leads
GET /getProjectList Get project list with IDs
POST /testWebhook Send dummy webhook events to your endpoint
Authentication - All operations require Ocp-Apim-Subscription-Key.
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
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. "Not Started"
Example
{
    "flag": "success",
    "message": "Project created successfully",
    "external_project_id": "EXT-PROJECT-10042",
    "projectID": 12345,
    "status": "Not Started"
  }
  • 400 Bad Request - missing/invalid fields, invalid projectName, or no matching users for projectLeadEmail.
  • 401 Unauthorized - missing subscription key.
  • 403 Forbidden - invalid or inactive subscription.

Code samples

curl --request POST \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/createProject' \
    --header 'Content-Type: application/json' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --data '{
      "projectName": "2026 Workers Comp 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
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

curl --request GET \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/getPotentialProjectLeads' \
    --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
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 Workers Comp 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

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

POST /testWebhook

Test webhook

POSTs 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". Finalist and winner dummies include supplier_id "supplier-0", domain "example.com", and businessName. Ignore these in production processing.

Request

Header Required Description
Ocp-Apim-Subscription-Key Yes Primary or secondary APIM subscription key. Resolves your business_id via public_api_business_credentials.
Content-Type No Use application/json if you send a body.

No path parameters. Body is optional. Omit eventType to send all seven event types.

Field Type Required Description
eventType string No One webhook event type. Also accepted as a query parameter. Omit to send all.

Allowed eventType values:

  • project.dates.updated
  • project.status.updated
  • project.participants.invited
  • project.participants.invite_rescinded
  • project.participants.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

# All seven event types
curl --request POST \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/testWebhook' \
    --header 'Content-Type: application/json' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --data '{}'

# One event type (JSON body)
curl --request POST \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/testWebhook' \
    --header 'Content-Type: application/json' \
    --header 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
    --data '{
      "eventType": "project.status.updated"
    }'

# One event type (query parameter)
curl --request POST \
    --url 'https://public-api-integration.azure-api.net/migration-public-api/testWebhook?eventType=project.dates.updated' \
    --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 POST /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.

Incoming requests are authenticated with a shared secret, not HMAC-SHA256 of the body and not Authorization: Bearer.

Item Value
Header x-webhook-secret
Scheme Shared secret token (static value). Compare it to the secret Catapult provides.
Body Raw JSON envelope. Not HMAC-signed.

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

1. Project date updates

eventType: project.dates.updated

Sent when project acceptance, question submission, or due dates change in Catapult.

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 Workers Comp 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 Workers Comp 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 Workers Comp RFP",
      "projectID": 12345,
      "acceptBy": "06/15/2026",
      "questionSubmissionDate": null,
      "dueDate": "06/30/2026"
    }
  }

2. Project status updates

eventType: project.status.updated

Sent when the human-readable project lifecycle status changes.

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 (see values below).

Project Status Values

Value Typical trigger
Not StartedNew project created, review round reset to not started.
InvitedParticipant invited.
Pending ApprovalProject sent for review round.
ApprovedRequestor review approved.
CompletedProject closed or winner selected.
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 Workers Comp RFP",
      "projectID": 12345,
      "projectStatus": "Invited"
    }
  }

3. Invitee information

Participant invite, rescind, and status-update events share the same payload shape. Status labels match the Invite Responders UI (SendGrid email events and Catapult responder outcomes). Use supplier_id to uniquely identify an invitee when multiple people share the same email domain.

Event types

eventType When
project.participants.invited New participant added.
project.participants.invite_rescinded Invite rescinded.
project.participants.status_updated Invitee status changed (SendGrid email event or Catapult status such as Accepted / Declined / Completed / Winner).

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 For invite/rescind: count of all active invitees. For status_updated: count of invitees included in this event (often the changed subset).
inviteeEmailDomains string[] Unique domains from the invitees in this payload, lowercased, sorted (e.g. ["acme.com","broker.com"]).
invitees object[] Per-invitee rows with supplier_id, domain, 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[].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: invited

Example
{
    "schemaVersion": 1,
    "eventId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "eventType": "project.participants.invited",
    "occurredAt": "2026-05-19T15:00:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Workers Comp RFP",
      "projectID": 12345,
      "totalInviteeCount": 3,
      "inviteeEmailDomains": ["acme.com", "contoso.com", "example.org"],
      "invitees": [
        { "supplier_id": "supplier-101", "domain": "acme.com", "status": "Status Pending" },
        { "supplier_id": "supplier-102", "domain": "contoso.com", "status": "Status Pending" },
        { "supplier_id": "supplier-103", "domain": "example.org", "status": "Status Pending" }
      ]
    }
  }

Example: invite rescinded

Same payload shape, only eventType changes.

Example
{
    "schemaVersion": 1,
    "eventId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "eventType": "project.participants.invite_rescinded",
    "occurredAt": "2026-05-19T15:30:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Workers Comp RFP",
      "projectID": 12345,
      "totalInviteeCount": 2,
      "inviteeEmailDomains": ["acme.com", "contoso.com"],
      "invitees": [
        { "supplier_id": "supplier-101", "domain": "acme.com", "status": "Email Opened" },
        { "supplier_id": "supplier-102", "domain": "contoso.com", "status": "Accepted" }
      ]
    }
  }

Example: status updated

Emitted for every SendGrid email event (delivered, dropped, deferred, bounce, open, click, spamreport, unsubscribe) and for Catapult responder status changes. Upsert by supplier_id. status is always the single latest value (Catapult wins over SendGrid).

Example - Email Bounced
{
    "schemaVersion": 1,
    "eventId": "d4e5f6a7-b8c9-0123-def0-234567890123",
    "eventType": "project.participants.status_updated",
    "occurredAt": "2026-05-19T16:05:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Workers Comp RFP",
      "projectID": 12345,
      "totalInviteeCount": 1,
      "inviteeEmailDomains": ["acme.com"],
      "invitees": [
        { "supplier_id": "supplier-101", "domain": "acme.com", "status": "Email Bounced" }
      ]
    }
  }
Example - Accepted (Catapult overrides prior email status)
{
    "schemaVersion": 1,
    "eventId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
    "eventType": "project.participants.status_updated",
    "occurredAt": "2026-05-19T17:00:00.000Z",
    "source": "catapulthq",
    "correlationId": null,
    "payload": {
      "external_project_id": "dertb242Sv",
      "projectName": "2026 Workers Comp RFP",
      "projectID": 12345,
      "totalInviteeCount": 1,
      "inviteeEmailDomains": ["contoso.com"],
      "invitees": [
        { "supplier_id": "supplier-102", "domain": "contoso.com", "status": "Accepted" }
      ]
    }
  }

4. 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 Workers Comp 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 Workers Comp 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" }
      ]
    }
  }

5. 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 Workers Comp 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 Workers Comp RFP",
      "projectID": 12345,
      "winnerCompanyNames": ["Acme Insurance"],
      "winners": [
        { "supplier_id": "supplier-101", "domain": "acme.com", "businessName": "Acme Insurance" }
      ]
    }
  }