Move a lead's card
PUT /
Moves the lead's pipeline card to another stage of its pipeline, the way a move on the board does.
Scope: pipeline:write
The card is the one GET /v1/leads/{lead_id} returns as card: for a member who may open only a follow-through card of the lead, that card, never the main card. stage, expected_stage, previous_stage, error.current_stage and error.valid_stages all refer to it. The move is recorded in the card's stage history and the pipeline's stage automations run exactly as when your team moves the card. A card already in stage answers 200 with changed: false whatever expected_stage says, so retrying after a timeout is safe.
Compare and set: send expected_stage with every automated move. The check and the move are one atomic write, so if someone moved the card since you read it, nothing changes and you get 409 stage_conflict with the card's current_stage. Read the lead again and decide again; do not resend the same move blindly.
Human placement wins. A move not made for an active team member (no acting_member_id, or one that is not an active member) never overrides a person: if the card was last placed by a team member, stage is one only a person may place cards in (the AI follow-up stage, ai-follow-up), or the card is a follow-through card (only a person moves those), the request fails with 409 member_required and nothing changes.
The lead must belong to a campaign the key may reach and be one the key's member can see in DialEcho, otherwise 404.
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 |
|---|---|---|---|
stage | string | Yes | Slug of the stage to move the card to, as card.stage and the pipeline board show slugs. It must be a stage of the card's pipeline. 1 to 200 characters after trimming. |
expected_stage | string | No | Slug of the stage you believe the card is in now. If it is in any other stage nothing changes and the request fails with 409 stage_conflict. Must not be blank. 1 to 200 characters after trimming. |
acting_member_id | UUID | No | The team member you are moving the card for, for example the member.id a verified lead-action token returned. If it is an active member of your organization the move is recorded as theirs, made through the API, exactly as if they had moved the card on the board; otherwise it is recorded as a move by the API. |
Response
Status: 200 OK
| Name | Type | Description |
|---|---|---|
lead_id | UUID | The lead. |
pipeline_lead_id | UUID | The card that was moved. |
stage | string | The card's stage now (the stage you sent). |
previous_stage | string | The stage the card was moved from; equals stage when nothing changed. |
changed | boolean | false when the card was already in stage: nothing was recorded and no automation ran. |
Example request
curl
bash
curl -X PUT "https://development.agencydevworks.ai/v1/leads/aaaaaaaa-0000-4000-8000-000000000001/stage" \
-H "Authorization: Bearer dei_your_key" \
-H "Content-Type: application/json" \
-d '{
"stage": "won",
"expected_stage": "appointment",
"acting_member_id": "ffffffff-0000-4000-8000-000000000001"
}'JavaScript
js
const response = await fetch("https://development.agencydevworks.ai/v1/leads/aaaaaaaa-0000-4000-8000-000000000001/stage", {
method: "PUT",
headers: {
"Authorization": "Bearer dei_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
"stage": "won",
"expected_stage": "appointment",
"acting_member_id": "ffffffff-0000-4000-8000-000000000001"
}),
});
const data = await response.json();Example response
Status: 200 OK
json
{
"lead_id": "aaaaaaaa-0000-4000-8000-000000000001",
"pipeline_lead_id": "dddddddd-0000-4000-8000-000000000001",
"stage": "won",
"previous_stage": "appointment",
"changed": true
}Errors
| HTTP | Code | When |
|---|---|---|
| 403 | insufficient_scope | The key does not have pipeline:write. Checked before the body is validated. |
| 404 | not_found | The lead does not exist or the key cannot reach it. |
| 409 | no_pipeline_card | The lead has no pipeline card. Nothing was moved. |
| 409 | stage_conflict | The card is not in expected_stage (or moved while the request ran). error.current_stage is its stage now. Nothing was moved. |
| 409 | member_required | A move not made for an active team member was refused because a person placed the card, only a person may place cards in stage, or the card is a follow-through card. error.current_stage is its stage. Nothing was moved. |
| 413 | payload_too_large | The body is larger than 1 MB. It was not read and nothing was written. |
| 422 | unknown_stage | stage is not a stage of the card's pipeline. error.valid_stages lists that pipeline's slugs in board order. |
| 422 | invalid_request | lead_id is not a UUID, or the body is invalid: stage missing or blank, a blank expected_stage, an acting_member_id that is not a UUID, or an unknown field. |
Every endpoint can also answer 401 invalid_api_key, 429 rate_limited and 503 dependency_unavailable. See Errors in the overview.
Related endpoints
- GET
/· Retrieve a leadv1/ leads/ {lead_id} - GET
/· Retrieve the pipelinev1/ campaigns/ {campaign_id}/ pipeline - POST
/· Verify a launch tokenv1/ lead-actions/ verify