Update a lead
PATCH /
Merges more details into a lead, typically from later steps of a form.
Scope: leads:write
Access: The key's member must be allowed to manage leads in the lead's campaign.
Takes the same optional details as POST /v1/leads, with the same limits and merge rules, plus deal_value. Custom field slugs are checked against the lead's campaign.
campaign_id and phone cannot be sent: a lead's phone number is its identity, so to capture a different number POST a new lead.
PATCH never creates or moves the lead's pipeline card and never changes a time zone the lead already has. custom_fields and deal_value are saved on the card the lead already has; if it has none, a PATCH carrying either fails with 409 no_pipeline_card and writes nothing. For a member who may open only a follow-through card of the lead, they are saved on the deal that card shares with the main card, and pipeline_lead_id is the follow-through card.
The lead must belong to a campaign the key may write to and be one the key's member can see in DialEcho, otherwise 404, checked before the member's permission to manage leads.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
lead_id | UUID | Yes | The lead, as POST /v1/leads returned it. |
Body
Content type: application/json
| Name | Type | Required | Description |
|---|---|---|---|
first_name | string | No | First name. Up to 100 characters. |
last_name | string | No | Last name. Up to 100 characters. |
email | string | No | An email address that can receive mail. Anything else, including an address over 254 characters, is rejected with field code email_invalid. Up to 254 characters. |
address | string | No | Street address. Up to 500 characters. |
city | string | No | City. Up to 100 characters. |
state | string | No | A US state name or 2-letter code; sets the lead's time zone when it has none. Up to 100 characters. |
postal_code | string | No | A US ZIP sets the lead's state (and its time zone, if it has none) when neither state nor address names one and none is stored. Up to 20 characters. |
company | string | No | Company name. Up to 200 characters. |
notes | string | No | Free-text notes on the lead. Up to 5,000 characters. |
custom_fields | object | No | Values for the campaign's own custom fields, keyed by field slug. Each value is a string, number, boolean or null. Saved on the lead's pipeline card as text, exactly as if your team typed it: true/false for checkbox fields, numbers as sent (12 becomes "12"), strings trimmed. A slug that is not one of the campaign's fields, or is a file or image field, is reported as custom_fields.<slug> with not_allowed (upload files with POST /v1/leads/{lead_id}/files); a bad value (another type, or a string too long) is reported once, as custom_fields. Up to 100 entries. Keys 1 to 100 characters; string values up to 2,000 characters. |
attribution | object | No | Marketing attribution, stored on the lead under the same key names and returned by the conversion report for ad-platform matching. Any key not listed here is rejected with not_allowed. |
attribution. | string | No | UTM source of the visit. Up to 500 characters. |
attribution. | string | No | UTM medium. Up to 500 characters. |
attribution. | string | No | UTM campaign. Up to 500 characters. |
attribution. | string | No | UTM term. Up to 500 characters. |
attribution. | string | No | UTM content. Up to 500 characters. |
attribution. | string | No | UTM id, for example the Meta campaign id. Up to 500 characters. |
attribution. | string | No | Ad set id of the ad the visitor came from. Up to 500 characters. |
attribution. | string | No | Ad id of the ad the visitor came from. Up to 500 characters. |
attribution. | string | No | Meta's click id from the landing page URL. Up to 500 characters. |
attribution. | string | No | The visitor's Meta _fbp cookie. Up to 500 characters. |
attribution. | string | No | The visitor's Meta _fbc cookie. Up to 500 characters. |
attribution. | string | No | Google's click id. Up to 500 characters. |
attribution. | string | No | The event ID your Meta Pixel's browser Lead event used for this submission, so a Lead sent from the conversion report deduplicates against it. Up to 200 characters. |
attribution. | string | No | The page the form was submitted on. Up to 2,048 characters. |
attribution. | string | No | Your name for the form or form step. Up to 200 characters. |
attribution. | string | No | The referring page. Up to 2,048 characters. |
deal_value | number | No | The deal value of the lead's pipeline card, as your team sees it on the card. 0 is a value and is saved; null or leaving it out keeps the stored value. The number is checked exactly as written in your JSON, never rounded first, so 100000000.000000001 is rejected and 12.500 is saved as 12.5. Read it back as card.deal_value from GET /v1/leads/{lead_id}. 0 to 100,000,000 with at most 2 decimal places. Strings and booleans are rejected. |
Response
Status: 200 OK
Identifiers only.
| Name | Type | Description |
|---|---|---|
lead_id | UUID | The lead. |
contact_id | UUID | null | The CRM contact the lead is linked to. |
pipeline_lead_id | UUID | null | The lead's card on the campaign's board, the card GET /v1/leads/{lead_id} returns as card (for a member who may open only a follow-through card of the lead, that card). null only when the lead could not be placed (for example the first stage only accepts cards placed by a person); sending the same POST again retries the placement. |
Example request
curl
bash
curl -X PATCH "https://development.agencydevworks.ai/v1/leads/aaaaaaaa-0000-4000-8000-000000000001" \
-H "Authorization: Bearer dei_your_key" \
-H "Content-Type: application/json" \
-d '{
"last_name": "Lee",
"address": "1 Market St",
"city": "San Francisco",
"postal_code": "94105",
"custom_fields": {
"roof_age": 12
},
"attribution": {
"form_name": "quote-step-2"
},
"deal_value": 12500.5
}'JavaScript
js
const response = await fetch("https://development.agencydevworks.ai/v1/leads/aaaaaaaa-0000-4000-8000-000000000001", {
method: "PATCH",
headers: {
"Authorization": "Bearer dei_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
"last_name": "Lee",
"address": "1 Market St",
"city": "San Francisco",
"postal_code": "94105",
"custom_fields": {
"roof_age": 12
},
"attribution": {
"form_name": "quote-step-2"
},
"deal_value": 12500.5
}),
});
const data = await response.json();Example response
Status: 200 OK
json
{
"lead_id": "aaaaaaaa-0000-4000-8000-000000000001",
"contact_id": "bbbbbbbb-0000-4000-8000-000000000001",
"pipeline_lead_id": "dddddddd-0000-4000-8000-000000000001"
}Errors
| HTTP | Code | When |
|---|---|---|
| 403 | insufficient_scope | The key does not have leads:write. Checked before the body is validated. |
| 403 | permission_denied | The key's member may not manage leads in the lead's campaign. Only for a lead they can see; any other lead is 404. |
| 404 | not_found | The lead does not exist or the key cannot reach it (other organization, a campaign outside the key, or a lead its member cannot see). |
| 409 | no_pipeline_card | custom_fields or deal_value were sent but the lead has no pipeline card (or it was deleted while the request ran). Nothing was written. |
| 413 | payload_too_large | The body is larger than 1 MB. It was not read and nothing was written. |
| 422 | invalid_request | lead_id is not a UUID, or the body is invalid: campaign_id or phone sent, unknown fields, values too long, an invalid email, unknown or file/image custom fields, or a deal_value outside its rules. error.fields lists every problem. |
Every endpoint can also answer 401 invalid_api_key, 429 rate_limited and 503 dependency_unavailable. See Errors in the overview.
Related endpoints
- POST
/· Create or update a leadv1/ leads - GET
/· Retrieve a leadv1/ leads/ {lead_id}