Deals
Create, read, update and delete Deals, and move them through pipeline stages.
A Deal is a sales opportunity with an Account. It sits at a stage in a pipeline.
The Deal object#
| Field | Type | Notes |
|---|---|---|
id |
string | UUID. |
name |
string | |
value |
number or null | In the workspace's currency (see currency on GET /me). |
value_period |
string | once_off, monthly, quarterly or yearly. Says whether value is a one-time or recurring amount. |
probability |
integer or null | Percent chance of winning, 0 to 100. |
expected_close_at |
string or null | ISO 8601 timestamp. |
actual_close_at |
string or null | ISO 8601 timestamp. Read-only through the API. |
stage_id |
string | The Deal's stage. See Stage IDs. |
account_id |
string | The Account the Deal is with. |
owner_id |
string or null | User ID of the Deal's owner. |
pipeline_id |
string or null | The pipeline the Deal is in. null means the workspace's default pipeline. |
created_at |
string | ISO 8601 timestamp. |
updated_at |
string | ISO 8601 timestamp. |
{
"id": "7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81",
"name": "Contoso renewal 2027",
"value": 48500,
"value_period": "yearly",
"probability": 60,
"expected_close_at": "2026-12-15T00:00:00.000Z",
"actual_close_at": null,
"stage_id": "rds_proposal",
"account_id": "3f6c2a1e-8b4d-4c2a-9f1e-2b7d5a9c4e10",
"owner_id": "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20",
"pipeline_id": null,
"created_at": "2026-11-12T09:30:00.000Z",
"updated_at": "2026-11-12T09:30:00.000Z"
}
Stage IDs#
A Deal's stage_id is a deal stage ID. It is not the id of a stage returned by GET /pipelines: pipeline stages are matched to deal stage IDs by their code (ignoring case).
The stage must belong to the Deal's pipeline, or to the workspace's default pipeline when the Deal has none. Otherwise the request answers 422 INVALID_STAGE.
Deal stage IDs for Signal's standard stage codes:
Pipeline stage code |
stage_id |
|---|---|
qualification |
rds_qualification |
discovery |
rds_discovery |
proposal |
rds_proposal |
negotiation |
rds_negotiation |
closed_won |
rds_closed_won |
closed_lost |
rds_closed_lost |
For any other stage (including stages you created), read stage_id from an existing Deal in that stage.
What a stage means (open, won or lost) comes from the pipeline stage's is_won and is_lost flags.
List Deals#
GET /deals · Scope: deals:read
Returns Deals newest first.
Query parameters#
| Parameter | Type | Required | Notes |
|---|---|---|---|
limit |
integer | No | 1 to 100. Default 20. |
cursor |
string | No | meta.cursor from the previous page. |
Example#
curl "https://crm.empiraa.app/api/v1/deals?limit=100" \
-H "Authorization: Bearer YOUR_API_KEY"
const res = await fetch("https://crm.empiraa.app/api/v1/deals?limit=100", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
const { data, meta } = await res.json();
Response 200#
{
"data": [
{
"id": "7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81",
"name": "Contoso renewal 2027",
"value": 48500,
"value_period": "yearly",
"probability": 60,
"expected_close_at": "2026-12-15T00:00:00.000Z",
"actual_close_at": null,
"stage_id": "rds_proposal",
"account_id": "3f6c2a1e-8b4d-4c2a-9f1e-2b7d5a9c4e10",
"owner_id": null,
"pipeline_id": null,
"created_at": "2026-11-12T09:30:00.000Z",
"updated_at": "2026-11-12T09:30:00.000Z"
}
],
"meta": { "cursor": null, "hasMore": false },
"error": null
}
Create a Deal#
POST /deals · Scope: deals:write · Supports Idempotency-Key
Request body#
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | Yes | |
account_id |
string | Yes | An Account in this workspace. |
stage_id |
string | Yes | A deal stage ID in the Deal's pipeline. See Stage IDs. |
pipeline_id |
string or null | No | A pipeline in this workspace. Leave out for the default pipeline. |
value |
number or null | No | |
value_period |
string | No | once_off (default), monthly, quarterly or yearly. |
probability |
integer | No | 0 to 100. Default 50. |
expected_close_at |
string or null | No | ISO 8601 date or timestamp. |
owner_id |
string or null | No | A user ID from GET /users. |
Example#
curl -X POST https://crm.empiraa.app/api/v1/deals \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8b0f6c1e-2d4a-4f7b-9c3e-5a1d7e9b2c40" \
-d '{
"name": "Contoso renewal 2027",
"account_id": "3f6c2a1e-8b4d-4c2a-9f1e-2b7d5a9c4e10",
"stage_id": "rds_proposal",
"value": 48500,
"value_period": "yearly",
"expected_close_at": "2026-12-15"
}'
const res = await fetch("https://crm.empiraa.app/api/v1/deals", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
name: "Contoso renewal 2027",
account_id: "3f6c2a1e-8b4d-4c2a-9f1e-2b7d5a9c4e10",
stage_id: "rds_proposal",
value: 48500,
value_period: "yearly",
expected_close_at: "2026-12-15",
}),
});
Response 201#
The new Deal object in data.
Errors#
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST |
name, stage_id or account_id is missing; pipeline_id isn't a string; or expected_close_at isn't a date. |
| 404 | NOT_FOUND |
The Account or pipeline doesn't exist in this workspace, or owner_id isn't an active member of the workspace. |
| 422 | INVALID_STAGE |
stage_id isn't a stage of the Deal's pipeline. |
Webhook events: deal.created.
Get a Deal#
GET /deals/{id} · Scope: deals:read
Path parameters#
| Parameter | Type | Notes |
|---|---|---|
id |
string | Deal ID. |
Example#
curl https://crm.empiraa.app/api/v1/deals/7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81 \
-H "Authorization: Bearer YOUR_API_KEY"
await fetch("https://crm.empiraa.app/api/v1/deals/7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
Response 200#
The Deal object in data.
Errors#
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
No Deal with that ID in this workspace. |
Update a Deal#
PATCH /deals/{id} · Scope: deals:write
Send only the fields you want to change.
Request body#
| Field | Type | Notes |
|---|---|---|
name |
string | |
value |
number or null | |
probability |
integer | 0 to 100. |
stage_id |
string | A deal stage ID in the Deal's pipeline. See Stage IDs. |
expected_close_at |
string or null | ISO 8601 date or timestamp. |
Other fields, including owner_id, value_period, pipeline_id and account_id, can't be changed through this endpoint and are ignored.
Example: move to a new stage#
curl -X PATCH https://crm.empiraa.app/api/v1/deals/7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"stage_id": "rds_negotiation", "probability": 80}'
await fetch("https://crm.empiraa.app/api/v1/deals/7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81", {
method: "PATCH",
headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ stage_id: "rds_negotiation", probability: 80 }),
});
Response 200#
The updated Deal object in data.
Errors#
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST |
expected_close_at isn't a date. |
| 404 | NOT_FOUND |
No Deal with that ID in this workspace. |
| 422 | INVALID_STAGE |
stage_id isn't a stage of the Deal's pipeline. |
Webhook events: deal.won when this request raises probability to 100 or more from below 100. Changing stage_id alone doesn't send deal.won.
Delete a Deal#
DELETE /deals/{id} · Scope: deals:write
Deletes the Deal permanently. This can't be undone.
Example#
curl -X DELETE https://crm.empiraa.app/api/v1/deals/7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81 \
-H "Authorization: Bearer YOUR_API_KEY"
await fetch("https://crm.empiraa.app/api/v1/deals/7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81", {
method: "DELETE",
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
Response 204#
No body.
Errors#
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
No Deal with that ID in this workspace. |
Webhook events: none.

