|
1
|
--- |
|
2
|
title: "POST /api/v7/sessions/{id}/transition — Advance State" |
|
3
|
sidebarTitle: "POST /transition" |
|
4
|
description: "Advance a session through the procurement state machine. Illegal moves return deterministic error codes. Every transition emits a timestamped audit event." |
|
5
|
--- |
|
6
|
|
|
7
|
ProcureNet manages each procurement session as a strict state machine. Calling this endpoint moves the session forward to a target state you specify. The engine validates that the transition is permitted from the session's current state — if it isn't, you receive a deterministic `400` error with a machine-readable rejection code so your integration can handle it without guesswork. Every successful transition also emits a timestamped audit event, giving you a complete, structured record of how a session progressed. |
|
8
|
|
|
9
|
## Authentication |
|
10
|
|
|
11
|
<Warning> |
|
12
|
This endpoint requires a valid Bearer JWT in the `Authorization` header. Requests without a token, or with an expired or malformed token, receive `401 Unauthorized`. |
|
13
|
</Warning> |
|
14
|
|
|
15
|
``` |
|
16
|
Authorization: Bearer <your_jwt> |
|
17
|
``` |
|
18
|
|
|
19
|
## Path Parameters |
|
20
|
|
|
21
|
<ParamField path="id" type="string" required> |
|
22
|
The session ID returned by `POST /api/v7/orchestrate`. Identifies the session whose state machine you are advancing. |
|
23
|
</ParamField> |
|
24
|
|
|
25
|
## Request Body |
|
26
|
|
|
27
|
<ParamField body="to" type="string" required> |
|
28
|
The target state to transition the session into. The engine enforces which transitions are valid from the session's current state and rejects any that are out of sequence. |
|
29
|
|
|
30
|
Common target states include: |
|
31
|
|
|
32
|
| State | Meaning | |
|
33
|
|---|---| |
|
34
|
| `pending_payment` | Buyer has confirmed their cart; awaiting funding method | |
|
35
|
| `escrow_pending` | High-value transaction detected; escrow acceptance required | |
|
36
|
| `payment_processing` | Funding method submitted; payment is in flight | |
|
37
|
| `completed` | Transaction finalized | |
|
38
|
| `cancelled` | Session terminated by buyer or system | |
|
39
|
</ParamField> |
|
40
|
|
|
41
|
## Request Example |
|
42
|
|
|
43
|
```typescript |
|
44
|
const sessionId = 'sess_abc123'; |
|
45
|
|
|
46
|
const res = await fetch( |
|
47
|
`https://api.procurenet.io/api/v7/sessions/${sessionId}/transition`, |
|
48
|
{ |
|
49
|
method: 'POST', |
|
50
|
headers: { |
|
51
|
'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...', |
|
52
|
'Content-Type': 'application/json' |
|
53
|
}, |
|
54
|
body: JSON.stringify({ to: 'pending_payment' }) |
|
55
|
} |
|
56
|
); |
|
57
|
|
|
58
|
const result = await res.json(); |
|
59
|
// { |
|
60
|
// "session_id": "sess_abc123", |
|
61
|
// "previous_state": "orchestrated", |
|
62
|
// "current_state": "pending_payment", |
|
63
|
// "transitioned_at": "2026-04-01T14:23:11Z" |
|
64
|
// } |
|
65
|
``` |
|
66
|
|
|
67
|
## Response Fields |
|
68
|
|
|
69
|
<ResponseField name="session_id" type="string" required> |
|
70
|
Echoes back the session ID for correlation in your logs. |
|
71
|
</ResponseField> |
|
72
|
|
|
73
|
<ResponseField name="previous_state" type="string" required> |
|
74
|
The state the session was in immediately before this transition. |
|
75
|
</ResponseField> |
|
76
|
|
|
77
|
<ResponseField name="current_state" type="string" required> |
|
78
|
The state the session has moved into as a result of this call. This will match the `to` value you submitted. |
|
79
|
</ResponseField> |
|
80
|
|
|
81
|
<ResponseField name="transitioned_at" type="string" required> |
|
82
|
ISO 8601 timestamp of when the transition was recorded. This is the same timestamp written to the transition audit event. |
|
83
|
</ResponseField> |
|
84
|
|
|
85
|
## Response Example |
|
86
|
|
|
87
|
```json |
|
88
|
{ |
|
89
|
"session_id": "sess_abc123", |
|
90
|
"previous_state": "orchestrated", |
|
91
|
"current_state": "pending_payment", |
|
92
|
"transitioned_at": "2026-04-01T14:23:11Z" |
|
93
|
} |
|
94
|
``` |
|
95
|
|
|
96
|
## Handling Illegal Transitions |
|
97
|
|
|
98
|
If you attempt a transition that is not valid from the session's current state, the engine returns `400` with a structured error body. Parse the `code` field to drive retry or fallback logic in your integration. |
|
99
|
|
|
100
|
```typescript |
|
101
|
if (!res.ok) { |
|
102
|
const err = await res.json(); |
|
103
|
// err.code === 'ILLEGAL_TRANSITION' |
|
104
|
// err.current_state === 'payment_processing' |
|
105
|
// err.attempted === 'pending_payment' |
|
106
|
console.error(`Cannot transition: ${err.message}`); |
|
107
|
} |
|
108
|
``` |
|
109
|
|
|
110
|
```json |
|
111
|
{ |
|
112
|
"code": "ILLEGAL_TRANSITION", |
|
113
|
"message": "Cannot move to 'pending_payment' from 'payment_processing'.", |
|
114
|
"current_state": "payment_processing", |
|
115
|
"attempted": "pending_payment" |
|
116
|
} |
|
117
|
``` |
|
118
|
|
|
119
|
<Info> |
|
120
|
Every successful transition — and every rejected attempt — is emitted as a structured audit event. You can use these events to reconstruct the full lifecycle of any session for debugging or compliance purposes. |
|
121
|
</Info> |
|
122
|
|
|
123
|
## Error Codes |
|
124
|
|
|
125
|
| HTTP Status | Code | Description | |
|
126
|
|---|---|---| |
|
127
|
| `400 Bad Request` | `ILLEGAL_TRANSITION` | The `to` state is not reachable from the session's current state. | |
|
128
|
| `400 Bad Request` | `UNKNOWN_STATE` | The `to` value is not a recognized session state. | |
|
129
|
| `401 Unauthorized` | — | Missing, expired, or invalid Bearer JWT. | |
|
130
|
| `404 Not Found` | — | No session exists for the provided `id`. | |
|
131
|
| `410 Gone` | `SESSION_EXPIRED` | The session has passed its TTL and can no longer be transitioned. Start a new session with `POST /api/v7/orchestrate`. | |