Update handover sub-stage

Move a contracted product line through the handover sub-stages: "Meet Pending", "Meet Scheduled", "Meet Rescheduled", "Meet Cancelled", "Meet Done" and "Drop off". "Meet Done" moves the product line to Onboarding and creates its checklist.

POSThttps://api.spyne.ai/user-management/v1/product-line/update-stage
AuthBearer token (console user)Rate limitNot documentedProcessingSynchronous; on Meet Done also calls the checklist creation (its failure does not fail the request).

This is a general stage endpoint for a rooftop (team), a product line (product_line), a product (product) or an add-on (addon). In this stage the console uses it with entityType: "product_line" and a sub_stage value, as described here. Stage changes (stage), live_date, and the team, product and add-on levels are not covered on this page.

For a product line sub-stage update, the call:

  • Loads the product line and its lifecycle configuration from the product line registry. The lifecycle lists the valid stages and sub-stages, the allowed transitions, the special transitions (for example a sub-stage that also changes the stage) and the meta_data fields each sub-stage requires.
  • Validates the sub-stage and its required meta_data fields, then checks that the move from the current sub-stage is allowed.
  • If the product line is already at the target sub-stage, returns 200 with skipped: true and changes nothing.
  • Writes the new stage and sub-stage to the product line and to its products. If the stage changes to Onboarding, it:
    • records the onboarding date,
    • sets an onboarding SLA from the platforms (it falls back to 7 if that fails),
    • assigns meta_data.ob_manager as the onboarding POC.
  • While the stage stays Onboarding, ob_manager and cs_manager / cs_id are still saved as POCs.
  • Writes a stage log entry.
  • Runs the sub-stage handler:
    • "Meet Done": calls Create the onboarding checklist with the caller's token. If that call fails, the failure is only logged and the response is still 200. It then recalculates the rooftop, dealer group and organisation stages.
    • "Meet Scheduled" / "Meet Rescheduled": no calendar event is created and the meeting details in meta_data are not stored. That handler is disabled in code. The rooftop stage is recalculated.
    • "Drop off": churns the product line. From Contracted this records "Sales Drop off", and from Onboarding "OB Drop off". It also runs the churn handling (contract amendment, notices). Churn is not covered on this page.
    • Other sub-stages: the rooftop stage is recalculated.
  • If the product line enters Onboarding, a separate fire-and-forget step clears demo conversation data for Sales Inbound.

Send the bearer token of a signed-in user in Authorization: Bearer YOUR_TOKEN. JWT and BASE64 tokens are both accepted and checked against the user records. The same token is forwarded to the lifecycle lookup and to the checklist call.

Before you call it

  • The contract must be Contracted, so the product line exists with stage Contracted.
  • You need the product line ID (entityId), the same value as product_line_details_id in Save handover details.
  • Valid sub-stages, allowed transitions and required meta_data fields come from the product line's lifecycle configuration. They are data, not code, so the exact rules per product line are to be confirmed against that configuration.
  • The product line record accepts these sub-stages: Meet Pending, Meet Scheduled, Meet Done, Meet Cancelled, Meet Rescheduled, OB To Be Scheduled, In Implementation, Under Review, Client Unresponsive, CSM Handover Pending, OB Live, Sales Drop off, OB Drop off.
  • In the console, "Meet Scheduled" and "Meet Rescheduled" are sent after Save handover details. "Meet Done" is sent after an onboarding manager is chosen.
  • The call is synchronous. It waits for the lifecycle lookup, the product line and product writes, the stage log and the sub-stage handler (including the checklist call on "Meet Done"). A failure in the checklist call does not fail the request. The rate limit is not documented.

Request

Requiredalways sendOnly onesend exactly one from the groupRequired ifrequired only in the case describedOptionalcan be left out
Request checklist
  1. Authenticate: send the Authorization: Bearer YOUR_TOKEN and Content-Type: application/json headers.
  2. Name the product line: set entityType to product_line and entityId to the product line ID.
  3. Pick the change: send exactly one of sub_stage or stage. This page uses sub_stage.
  4. Add the meta data: add the meta_data fields your sub-stage needs (see below).
  5. Check for a skip: check data.skipped. When it is true, nothing changed.

The console sends these meta_data fields per sub-stage:

  • "Meet Scheduled" / "Meet Rescheduled": start_time, end_time, time_zone, attendees, meet_required: true, plus notes for a reschedule.
  • "Meet Done": ob_manager, and meet_required: false when no call was needed.
  • "Meet Cancelled": enterprise_id, team_id, team_name, notes.
  • "Drop off" from Onboarding: churn_reason is required.

Headers

ParameterTypeRequiredDescription
AuthorizationstringRequiredBearer YOUR_TOKEN
Content-TypestringRequiredapplication/json

Body

ParameterTypeRequiredDescription
entityTypestringRequiredproduct_line for this use. Also accepts product, team and addon (not covered here).
entityIdstringRequiredProduct line ID when entityType is product_line.
Change to make—RequiredSend sub_stage or stage, not both.
sub_stagestringOnly oneNew sub-stage, for example Meet Scheduled or Meet Done. Must be valid in the lifecycle configuration.
stagestringOnly oneNew stage. Not covered on this page.
meta_dataobjectRequired ifThe sub-stage requires fields. Fields listed as required for the sub-stage in the lifecycle configuration must be present and non-empty.
meta_data.ob_managerstringOptionalOnboarding manager (user ID from the onboarding POC list). Saved as the product line's onboarding POC when the stage is or becomes Onboarding.
meta_data.cs_manager, cs_idstringOptionalCustomer success manager. Saved as the CS POC while in Onboarding or Live.
meta_data.platformsstring[]OptionalUsed for the onboarding SLA when the stage becomes Onboarding. Falls back to the platforms saved by the handover call.
meta_data.churn_reasonstringRequired if"Drop off" from Onboarding. Reason for the drop-off.
meta_data.start_time, end_time, time_zone, attendees, meet_required, notes, team_name, enterprise_id, team_idvariousOptionalSent by the console for meeting sub-stages. They are checked only if the lifecycle configuration marks them required. They are not stored by this endpoint.
meta_data.reasonstringOptionalUsed as the stage log reason.
live_datestringOptionalYYYY-MM-DD, only with stage: "Live". Not covered on this page.

Sample request

curl -X POST "https://api.spyne.ai/user-management/v1/product-line/update-stage" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entityType": "product_line",
    "entityId": "PRODUCT_LINE_ID",
    "sub_stage": "Meet Done",
    "meta_data": { "ob_manager": "USER_ID", "meet_required": false }
  }'
import requests

res = requests.post(
    "https://api.spyne.ai/user-management/v1/product-line/update-stage",
    json={
        "entityType": "product_line",
        "entityId": "PRODUCT_LINE_ID",
        "sub_stage": "Meet Scheduled",
        "meta_data": {
            "start_time": "2026-01-01T15:00:00.000Z",
            "end_time": "2026-01-01T15:30:00.000Z",
            "time_zone": "America/New_York",
            "attendees": ["[email protected]"],
            "meet_required": True,
        },
    },
    headers={"Authorization": "Bearer YOUR_TOKEN"},
)
print(res.status_code, res.json())
const res = await fetch("https://api.spyne.ai/user-management/v1/product-line/update-stage", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_TOKEN", "Content-Type": "application/json" },
  body: JSON.stringify({
    entityType: "product_line",
    entityId: "PRODUCT_LINE_ID",
    sub_stage: "Meet Done",
    meta_data: { ob_manager: "USER_ID" },
  }),
});
console.log(res.status, await res.json());

Sample response

{
  "message": "Stage/substage updated successfully",
  "error": false,
  "code": "",
  "details": null,
  "data": {
    "level": "product_line",
    "entityId": "PRODUCT_LINE_ID",
    "team_id": "TEAM_ID",
    "enterprise_id": "ENTERPRISE_ID",
    "product_details_id": null,
    "product_line_details_id": "PRODUCT_LINE_ID",
    "previousStage": "Contracted",
    "previousSubStage": "Meet Scheduled",
    "newStage": "Onboarding",
    "newSubStage": "<subStage>",
    "parentUpdates": null,
    "updatedProductLineDetails": 1,
    "updatedProductDetails": 2
  }
}
{
  "message": "Already at substage \"Meet Scheduled\". No update needed.",
  "error": false,
  "code": "",
  "details": null,
  "data": {
    "level": "product_line",
    "entityId": "PRODUCT_LINE_ID",
    "currentStage": "Contracted",
    "currentSubStage": "Meet Scheduled",
    "skipped": true
  }
}
{
  "message": "Cannot transition from substage \"Meet Pending\" to \"Meet Done\". Allowed transitions: <allowedSubStages>",
  "error": true,
  "code": "ERROR_BAD_REQ_400",
  "details": null,
  "data": null
}
{ "message": "Invalid Token", "error": true, "code": "ERROR_AUTH_401", "details": null, "data": null }
{
  "message": "Internal server error while updating stage",
  "error": "<error message>",
  "code": "",
  "details": null,
  "data": null
}

Response parameters

ParameterTypeDescription
data.levelstringproduct_line.
data.entityIdstringProduct line ID.
data.team_idstringRooftop ID.
data.enterprise_idstringDealer group ID.
data.product_line_details_idstringProduct line ID.
data.product_details_idnullNot used at product line level.
data.previousStage, data.previousSubStagestringStage and sub-stage before the update.
data.newStage, data.newSubStagestringStage and sub-stage the update targeted. For "Meet Done", the stage comes from the lifecycle configuration (the console expects Onboarding). The exact newSubStage after "Meet Done" is to be confirmed against the configuration.
data.updatedProductLineDetailsnumber1 when the product line was written.
data.updatedProductDetailsnumberRows written for the product line's products.
data.parentUpdatesnullNot set at product line level.
data.churnAmendmentContractIdstringPresent only when a drop-off created a contract amendment.
data.skipped, data.currentStage, data.currentSubStageboolean, stringPresent only on the "no update needed" response.

Errors

StatusMessageWhen it happens
400(framework)entityType or entityId is missing, or entityType is not product, product_line, team or addon. The body is not documented in code.
400Either stage or sub_stage must be providedNeither was sent.
400Only one of stage or sub_stage can be updated at a timeBoth were sent.
400Product line details not found for id: IDNo product line has this ID. This is 400, not 404.
400Invalid substage "X". Valid substages: ...The sub-stage is not in the lifecycle configuration.
400Required fields missing for substage "X": ...A meta_data field the lifecycle configuration requires is missing or empty.
400churn_reason is required when Drop off is triggered from Onboarding"Drop off" from Onboarding without churn_reason.
400Cannot transition from substage "A" to "B". Allowed transitions: ...The move is not allowed by the lifecycle configuration.
400Cannot move to OB Live. All onboarding tasks must be completed first.sub_stage is OB Live and a required checklist task is not COMPLETED.
401Invalid or missing tokenNo Bearer token, or the token failed validation.
500Failed to fetch lifecycle configuration from ...The lifecycle lookup failed. The message ends with an internal service name. In this response code is the number 500.
500Internal server error while updating stageAny other error. The underlying message is in the error field.

What happens next

After "Meet Done", the product line is in Onboarding and its checklist exists. Check it with the endpoints in Checklist. If you call Create the onboarding checklist again for the same product line, it skips tasks that already exist.

Called before: Save handover details. Called after: Create the onboarding checklist, triggered automatically on "Meet Done".


Did this page help you?