Skip to main content

PATCH /moves/:id/workflows/:slot/data

Merge integration-owned keys into a move's workflow data for a given slot. Editability is driven by the move's workflow definition: a key can be written through this endpoint only when the corresponding step in the move's pickup_workflow.steps or delivery_workflow.steps has config.allow_server_edits set to true.

The pickup workflow reads these keys to gate driver actions (e.g., a loaner cannot leave the lot until consumer-info-complete is true).

This endpoint is idempotent and safe to retry.

Terminology: the move's "rooftop" and the move's customer_id refer to the same thing. The bearer token's x-hasura-allowed-customers claim is checked against this value.

Request
curl --request PATCH \
--url https://api.hopdrive.com/v1/moves/:id/workflows/:slot/data \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <your token>' \
--data '{ "consumer-info-complete": true }'

Example Request

PATCH /v1/moves/10033/workflows/pickup/data
{
"consumer-info-complete": true
}

Example Request — Multiple Keys

PATCH /v1/moves/10033/workflows/pickup/data
{
"consumer-info-complete": true,
"internal-reference": "ABC-123"
}

URL Params

FieldTypeRequired?Description
idNumberRequiredThe move's numeric ID.
slotStringRequiredThe workflow slot to merge into. "pickup" or "delivery".

Headers

HeaderRequired?Description
Content-TypeRequiredMust be application/json.
AuthorizationRequiredBearer token from POST /v1/authorize.

Body

The body is a flat JSON object whose keys map to editable step IDs in the slot's workflow. A step is editable only when its config.allow_server_edits is true. The value's type must match the step's type (boolean, string, number, or integer); a wrong-typed value returns 422.

To discover which keys are editable for a given move, call GET /v1/moves/:id/workflows/:slot and inspect the returned steps[*] for entries whose config.allow_server_edits is true. Each such entry's id is a writable key in this endpoint, and its type constrains the value.

In the standard pickup workflow, consumer-info-complete is opted in this way (boolean). Body keys without a matching server-editable step are returned in the response's ignored_keys array — useful for catching typos.

Behavior

  • The lifecycle lock is per-slot:
    • Pickup slot is editable while the move is pre-pickup. Once the status reaches pickup successful, delivery started, delivery arrived, or delivery successful, pickup writes are rejected with 409.
    • Delivery slot is editable through the in-flight statuses (pickup successful, delivery started, delivery arrived) so integrators can update consumer info while the driver is en route. Only delivery successful locks the delivery slot.
  • A move with a non-null cancel_status rejects writes to either slot.
  • If the slot's column is null, it is initialized with the provided keys.
  • If the column already contains other keys, the provided keys are merged in without disturbing them.
  • Body keys that don't match a server-editable step are surfaced in ignored_keys. If every key in the body is ignored, the request is rejected with 422 and the ignored keys are listed in the error message.

Idempotency

This endpoint is idempotent. Resending the same body produces the same state. If every key in the request already matches the stored value, the response returns 200 with the existing updated_at — no row write, and no downstream triggers fire. Safe to retry on transport errors.

Concurrency

Last writer wins on overlapping keys; non-overlapping keys are preserved. There is a small TOCTOU window between the read and the write: if the column is null at read time and a concurrent write populates it before this request commits, the concurrent value can be clobbered. In practice this is only a concern if integration writes race against driver writes on the same move and slot — avoid that pattern.

Example Response

200 PATCH /v1/moves/10033/workflows/pickup/data
{
"id": 10033,
"slot": "pickup",
"workflow_data": {
"consumer-info-complete": true
},
"ignored_keys": [],
"updated_at": "2026-04-28T14:05:00+00:00"
}

Response Fields

FieldTypeDescription
idNumberThe move's numeric ID.
slotStringEchoes the slot URL parameter ("pickup" or "delivery").
workflow_dataObjectThe slot's workflow data, filtered to keys whose corresponding step has allow_server_edits: true. Driver-recorded keys are not surfaced.
ignored_keysArrayBody keys that did not match a server-editable step in the slot's workflow. Use to catch typos. Empty when all body keys matched.
updated_atStringISO 8601 timestamp of the move's last update. Unchanged on idempotent no-op writes.

Error Responses

All errors follow this envelope:

{
"errors": [
{ "type": "invalid_request_error", "code": "REQUEST_INVALID", "message": "..." }
]
}
StatusDescription
400Invalid move ID.
401Missing or invalid authorization token.
403The bearer token is malformed, or its allowed-customers claim does not include the move's rooftop.
404Move not found, or slot is not a known workflow slot.
409The slot's lifecycle is locked: pickup is past pickup-successful, delivery is at delivery-successful, or the move has been canceled.
422Request body has no editable fields after matching against the workflow steps, or a value's type doesn't match the step's type. When all body keys were ignored as typos, the message lists them.
500Unexpected server error.