Send CLEATUS opportunities to your CRM with webhooks
Push matching opportunities and hot leads from CLEATUS into your own CRM or alerting system using a Workflow webhook node.
Use a CLEATUS Workflow to POST opportunity data to any HTTPS endpoint the moment a new match is found, a contract enters your pipeline, or a pursuit changes phase. This is how you feed CLEATUS opportunities into your own CRM, alert your team on hot leads, or drive any downstream system that accepts a webhook.
Who this is for
Teams that want CLEATUS to push opportunity data out to their own systems (a custom CRM, a Slack/Teams alert bot, an internal API) instead of, or alongside, working inside the CLEATUS dashboard. If you instead want a no-code path into 5,000+ apps, see the Zapier integration; webhooks are the direct option.
What you'll need
- A CLEATUS plan that includes AI Workflows.
- An HTTPS endpoint in your CRM (or middleware) that can receive a JSON POST. It must be a public HTTPS URL.
- Access to build workflows in your CLEATUS workspace (Dashboard → Workflows).
How it works
A webhook in CLEATUS is a node inside a Workflow, not a standalone API subscription. The flow is:
- A trigger starts the workflow (for example, "New recommendation").
- Optional condition nodes (If / Else, Filter) narrow which items proceed.
- A Webhook node makes an outbound HTTP request to your endpoint with a JSON body.
CLEATUS is the sender; your CRM is the receiver. You provide the URL and the authentication headers.
Note:
Direction matters: this guide covers CLEATUS sending data to you. There is a separate inbound "Webhook" trigger
that lets an external system POST into CLEATUS to start a workflow, which is a different use case.
1. Webhook API documentation
Authentication and configuration
The Webhook node authenticates to your endpoint with HTTP headers you configure. There are two kinds:
| Header type | Use it for | Behavior |
|---|---|---|
| Headers | Non-sensitive values (Content-Type is set for you) | Sent as-is. |
| Secret Headers | API keys, bearer tokens | Encrypted at rest (AES-256-GCM) and decrypted only at send time. After saving, the value is masked in the editor. |
To authenticate to most CRMs, add a Secret Header such as Authorization with value Bearer <your-token>, or X-API-Key with your key. CLEATUS sends whatever headers you configure.
Note:
CLEATUS does not sign outbound webhook requests (there is no HMAC signature or shared-secret request signing).
Your endpoint should authenticate the request by the API key / bearer token you supply as a Secret Header, and
should be reachable only over HTTPS. If your security model requires signature verification, that is a current gap,
tell us and we'll scope it.
Endpoint specification
You supply the endpoint. CLEATUS enforces a few rules on it:
- HTTPS only. Non-HTTPS URLs are rejected.
- Public hosts only. For safety, CLEATUS resolves your hostname and blocks private/internal IP ranges and cloud metadata endpoints, and pins the resolved IP for the request (SSRF protection). Your CRM endpoint must resolve to a public IP.
- No redirects. A
3xxredirect response is treated as a failure, point the node at the final URL.
Request format and structure
| Property | Value |
|---|---|
| Method | GET, POST, PUT, PATCH, or DELETE. Default POST. |
| Content-Type | application/json, set automatically for any method that carries a body. |
| Body | The default structured JSON payload (see section 2). |
| Batching | If a workflow run produces multiple items, they are sent in a single request (the body becomes a JSON array). |
Response handling
- Any non-2xx response fails the node.
- The response body (up to 100 KB) is captured. If a later node in the same workflow needs your CRM's reply (for example, a returned record id), it is available as
response.status,response.ok, andresponse.body.
Rate limits and SLA
- No CLEATUS-imposed rate limit on outbound webhook calls.
- Failure handling is set at the workflow level, not per node. A workflow has retry-attempt and retry-delay settings and an on-failure policy (for example, notify), plus a "continue on step failure" option. Because delivery can be retried and a run fires once per triggering event, design your endpoint to be idempotent (dedupe on
contractIdorsolicitationNumber). - Delivery is best-effort at workflow execution time; there is no separate delivery-retry queue or contractual uptime SLA on webhook delivery.
2. Available payload fields
When you add a Webhook node in the workflow builder, it sends a default structured payload: a JSON object with the trigger data nested under a trigger key. The exact fields depend on the trigger you choose. The New recommendation trigger is the best fit for "push new matches to my CRM" because it is the only trigger that carries the match score.
Default payload for the "New recommendation" trigger
{
"trigger": {
"entityId": "…",
"contractId": "…",
"bestQualityScore": 87,
"title": "…",
"agencyName": "…",
"solicitationNumber": "…",
"naicsId": "…",
"type": "solicitation",
"setAside": "SDVOSBC",
"state": "TX",
"postedDate": "2026-07-20T00:00:00.000Z",
"deadlineDate": "2026-08-15T00:00:00.000Z"
}
}
| Requested field | In this payload | Field | Notes |
|---|---|---|---|
| Match score | Yes | bestQualityScore | 0–100 fit score. This is the match score. |
| Contract/solicitation title | Yes | title | |
| Agency name | Yes | agencyName | |
| Solicitation number | Yes | solicitationNumber | |
| NAICS code | Yes | naicsId | |
| Notice type | Yes | type | e.g. solicitation, sources-sought |
| Set-aside | Yes | setAside | e.g. SDVOSBC, 8(a), WOSB |
| Place of performance | Yes | state | City/state code |
| Posted date | Yes | postedDate | ISO 8601 |
| Opportunity deadline | Yes | deadlineDate | ISO 8601 |
| Contract description | No | not on the trigger payload | See the limitation below. |
| Contract value | No | not on the trigger payload | See the limitation below. |
Fields on other triggers
Each trigger carries its own field set. Some highlights (reference as {{ trigger.<field> }} when a custom body is configured):
- Contract added to pipeline:
title,agencyName,solicitationNumber,naicsCode,responseDeadline,deadlineDate,url, pluspursuitIdandscore. - Pursuit phase changed: the same core contract fields plus
previousColumnTitle/newColumnTitle(the phase moved from/to) andpursuitId. - Saved search match:
title,agencyName,solicitationNumber,type,setAside,postedDate,deadlineDate, plussavedQueryName. (It does not carrynaicsCode,responseDeadline, orurl.)
Field availability is per trigger; a variable that a given trigger does not supply resolves to an empty string.
Note:
Contract value and full description are not in the webhook payload today. The estimated contract value (a
low/high range that is itself frequently empty, most notices publish no dollar figure pre-award) and the long-form
description live on the contract record, but the workflow builder does not currently expose a data-lookup node to
fold them into a webhook body. If you need value or description in the push, tell us, it's a known gap we're
tracking.
3. Custom fields and payload shape
What ships today
The Webhook node sends the default structured payload shown in section 2. That payload already carries the standard opportunity fields (agency, title, solicitation number, NAICS, set-aside, deadline, and the match score), so many CRMs can consume it directly by reading the nested trigger object.
What is not self-serve yet
The workflow builder's Webhook editor lets you set the URL, method, and headers, but it does not currently expose a field to author a custom request body. That means, in the self-serve UI today, you cannot:
- rename the payload keys to match your CRM's schema,
- add a custom field such as a
goNoGoboolean, or - fold in extra data (contract value, full description) from a lookup node.
If you need a custom-shaped body or custom fields, tell us the exact JSON your CRM expects and we'll help configure the webhook body for your workflow. The templating capability below is what powers that configuration.
Templating syntax (for a configured custom body)
When a custom body is configured, values are filled with {{ … }} expressions:
| Expression | Resolves to |
|---|---|
{{ trigger.<field> }} | A field from the trigger payload |
{{ nodes.<NodeName>.json.<field> }} | Output of a named upstream node |
{{ json.<field> }} | The current item's data |
Unresolved variables render as an empty string. (Server environment variables are intentionally not accessible.)
Example custom body (New recommendation trigger)
Rename keys to whatever your CRM expects; a constant like goNoGo is just a literal you include:
{
"source": "CLEATUS",
"cleatusContractId": "{{ trigger.contractId }}",
"title": "{{ trigger.title }}",
"agency": "{{ trigger.agencyName }}",
"solicitationNumber": "{{ trigger.solicitationNumber }}",
"naicsCode": "{{ trigger.naicsId }}",
"noticeType": "{{ trigger.type }}",
"setAside": "{{ trigger.setAside }}",
"placeOfPerformance": "{{ trigger.state }}",
"postedDate": "{{ trigger.postedDate }}",
"deadline": "{{ trigger.deadlineDate }}",
"matchScore": "{{ trigger.bestQualityScore }}",
"goNoGo": true
}
Every {{ trigger.* }} value above is a real field the New recommendation trigger supplies.
4. Implementation guide
Build the workflow
- Go to Dashboard → Workflows and create a new workflow.
- Set the trigger. For pushing new matches, pick New recommendation. Other options: Contract added to pipeline, Pursuit phase changed, Saved search match, or Schedule.
- (Optional) Add conditions. Insert If / Else and Filter nodes between the trigger and the webhook to branch on any field, for example, only continue when the match score clears a bar, the agency matches a target list, or the set-aside qualifies you. A branch that receives no items does not fire the webhook.
- Add a Webhook action node and configure:
- Endpoint URL: your CRM endpoint (HTTPS).
- Method: usually
POST. - Headers / Secret Headers: put your
Authorization: Bearer …orX-API-Key: …under Secret Headers.
- Activate the workflow.
The node will POST the default structured payload (section 2) to your endpoint whenever the trigger fires and the item clears your conditions.
Trigger on conditions or phases
- On a phase change: use the Pursuit phase changed trigger. It fires when a pursuit moves to a different pipeline column and carries the previous/new column names, so you can push "moved to Bidding" alerts.
- On a condition: use If / Else and Filter condition nodes as in step 3.
Testing and validation
- Point the URL at a test bin first. Use a service like
webhook.siteor your own staging endpoint as the URL, then run the workflow and inspect the exact JSON CLEATUS sends. - Run the workflow and open the run log. The Webhook node logs the method, URL, item count, and the response status it received.
- Watch for failures. A non-2xx response fails the node. Confirm your endpoint returns a 2xx on success.
- Swap in the real endpoint once the payload looks right, and confirm the record lands in your CRM.
Current limitations (be aware)
- No custom request body in the self-serve UI. The webhook node sends the default structured payload; renaming keys, adding custom fields (e.g. a go/no-go boolean), or reshaping the body needs CLEATUS to configure the workflow for you today.
- Contract value and full description are not available in the webhook payload (no data-lookup node is exposed in the builder to include them).
- No request signing (HMAC). Authenticate via the API key/token you set as a Secret Header.
- Match score is only on the "New recommendation" trigger (and a
scoreon "Contract added to pipeline"). Other triggers don't carry it. - Contract value, when it is exposed elsewhere, is an estimated range and is often empty (most notices don't publish a dollar figure pre-award).
- Failure handling is workflow-level (retry attempts/delay and an on-failure policy), not a per-node toggle. Keep your endpoint idempotent; there is no contractual delivery SLA.
Support
- Email: support@cleat.ai
- Related: Zapier integration · MCP Server
