Skip to main content
Restricted access. Public API v2 is currently available only to allowlisted organizations. Requests from workspaces that have not been enabled are rejected. Contact your Reevo representative to have your organization added to the allowlist.

Manage Stages

This is the authoritative guide for changing stages in Public API v2. There is no dedicated stage-shift endpoint. Change stages by patching the stage attribute on the record. This applies uniformly to contact, account, and opportunity: each exposes a writable stage attribute. Account stage is backed internally by a different column, but the public write key is stage — there is no current_stage_id field on the public API. For the read, write, and filter shape of stage values themselves, see Selects and Stages.

Contact stages

curl -X PATCH "https://api.reevo.ai/api/v2/public/contact/<contact_id>" \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "attributes": {
      "stage": { "value_type": "select", "value": { "api_name": "working" } }
    }
  }'
Stage changes can be blocked by entrance criteria. This applies to contact, opportunity, and account stage moves: when a move is blocked, the API returns 422 stage_entrance_criteria_not_met.

Opportunity stages

Opportunity stage is required when creating an opportunity.
{
  "attributes": {
    "display_name": "Acme - Enterprise Deal",
    "stage": { "value_type": "select", "value": { "api_name": "proposal" } },
    "account_id": "<account_id>",
    "amount": 150000
  }
}
Patch the stage attribute to move an opportunity:
curl -X PATCH "https://api.reevo.ai/api/v2/public/opportunity/<opportunity_id>" \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "attributes": {
      "stage": { "value_type": "select", "value": { "api_name": "closed_won" } }
    }
  }'
Opportunity stage writes accept the option id, api_name, or display_name (a bare string is matched as a display name). When the same stage name exists in more than one pipeline, add select_list_id to the option reference to pin the pipeline whose stage you mean — for example { "select_list_id": "<pipeline stage list id>", "api_name": "closed_won" }. Referencing by id is unambiguous on its own.

Closing an opportunity

Moving an opportunity into a closing stage requires a close reason in the same request:
  • A move to a closed-won stage requires primary_closed_won_reason.
  • A move to a closed-lost stage requires primary_closed_lost_reason.
These reasons are required only on the closing transition, not on create, so they are not flagged as required-on-create in GET /api/v2/public/_schema/opportunity. Discover the valid reasons there: each pipeline’s stage list in the schema carries closed_won_reasons and closed_lost_reasons — the reason options for that specific pipeline. Send the matching reason (by option id) in the same patch that moves the opportunity to the closing stage; omitting it rejects the stage change.

Moving between pipelines

An opportunity belongs to one pipeline at a time. Patching stage to a stage that lives in a different pipeline migrates the opportunity into that pipeline. A pipeline move has two rules:
  • Only the target pipeline’s initial stage may be set in the move. Set stage to the first (initial) stage of the destination pipeline; setting any later stage returns 400 field_value_invalid (“Only the initial stage of the target pipeline may be set…”). Advance the opportunity further with a follow-up patch once it is in the new pipeline.
  • Close reasons resolve against the target pipeline. If the move is also a close, send primary_closed_won_reason / primary_closed_lost_reason using option ids from the destination pipeline’s reason lists (see above).
A destination pipeline with no active stage, or a move the pipeline rejects, also returns 400 field_value_invalid with the reason in details.

Read-only opportunity fields

Do not write these fields directly:
  • closed_at
  • stage_last_shifted_at
They are rendered on reads where available, but direct writes are rejected as unknown_field (the API does not expose them as writable attributes). closed_at is stamped when the opportunity first enters a closing stage and is not re-stamped if it later moves to a different closing stage. stage_last_shifted_at, by contrast, advances on every stage change. An opportunity’s won/lost outcome is not a writable top-level field. It is exposed through the stage value’s outcome_state (OPEN, CLOSED_WON, or CLOSED_LOST) and changes only as a side effect of moving the opportunity to a closing stage. There is no top-level outcome or status attribute to write; sending one is rejected as unknown_field.

Common errors

Error codeUsually meansHow to recover
request_validation_failedA required standard field (display_name, stage, account_id) was omitted on opportunity create (422), or a required standard field’s value failed type validation — for example an explicit stage: null (400). details.loc/type/all_errors name the field.Include every required standard field in the create request, and send a real value rather than null
field_required_missingA required custom field was omitted on opportunity create. Returns 400.Include the required custom field
field_value_invalidA stage value could not be resolved (for example an unknown stage id), or a pipeline move was invalid — a non-initial target stage, a destination pipeline with no active stage, or a move the pipeline rejected (returns 400); details carries the reasonSet the target pipeline’s initial stage (then shift in a follow-up patch), or use a valid stage id
unknown_fieldRequest tried to write a read-only or system-managed stage-related field (such as closed_at, stage_last_shifted_at, or a top-level outcome/status) — these are not exposed as writable attributesRemove read-only fields from the request
stage_entrance_criteria_not_metA contact, opportunity, or account stage move is blocked by criteriaFix the missing prerequisites before retrying