POST Pick Up Authorization Code
Collect the authorization code after a member allows your app on hosted Connect. This endpoint is for flows created with delivery: pickup. It is safe to poll every few seconds or call after an authorization.granted event.
Endpoint
POST https://app.crosstrade.io/v1/api/partner/users/{external_id}/authorizations/{flow_id}/code
Headers
| Name | Required | Value |
|---|---|---|
| Authorization | Yes | Bearer <partner access token> with partner:provision |
No Idempotency-Key is required. Send no body, including no empty JSON object. There are no query parameters.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
external_id | string | Yes | Member id used at creation |
flow_id | string | Yes | The connect.flow_id returned by create, connect-link or reopen |
Code examples
Set PARTNER_TOKEN and FLOW_ID, and replace th_8831 with your member id. These examples make one pickup request; 202 means wait before trying again.
- Python
- JavaScript
- cURL
import os
import requests
flow_id = os.environ["FLOW_ID"]
response = requests.post(
f"https://app.crosstrade.io/v1/api/partner/users/th_8831/authorizations/{flow_id}/code",
headers={"Authorization": f"Bearer {os.environ['PARTNER_TOKEN']}"},
timeout=15,
)
response.raise_for_status()
authorization = response.json()["authorization"]
print(response.status_code, authorization["status"])
if authorization["status"] == "code_ready":
code = authorization["code"] # Exchange server-side; do not log the code.
const flowId = process.env.FLOW_ID;
const response = await fetch(
`https://app.crosstrade.io/v1/api/partner/users/th_8831/authorizations/${encodeURIComponent(flowId)}/code`,
{
method: "POST",
headers: { Authorization: `Bearer ${process.env.PARTNER_TOKEN}` },
},
);
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const { authorization } = await response.json();
console.log(response.status, authorization.status);
// When code_ready, exchange authorization.code server-side with the stored verifier.
curl -sS -X POST \
"https://app.crosstrade.io/v1/api/partner/users/th_8831/authorizations/$FLOW_ID/code" \
-H "Authorization: Bearer $PARTNER_TOKEN"
Responses
- 202 Pending
- 200 Code Ready
- 200 Exchanged
The member has not allowed the app yet.
{
"success": true,
"authorization": {
"status": "pending",
"flow_id": "pcf_a1b2c3d4e5f6"
},
"meta": {
"requestId": "preq_1a2b3c4d5e6f7a80"
}
}
Exchange this code at POST /v1/oauth/token using HTTP Basic, the original redirect URI and the PKCE verifier. The code expires 300 seconds after issuance; pickup does not extend its lifetime. The flow must also remain open.
{
"success": true,
"authorization": {
"status": "code_ready",
"flow_id": "pcf_a1b2c3d4e5f6",
"code": "xt_ac_...",
"expires": "2026-10-15T14:13:52Z"
},
"meta": {
"requestId": "preq_8f7e6d5c4b3a2910"
}
}
The code was already exchanged. Credentials are not returned again.
{
"success": true,
"authorization": {
"status": "exchanged",
"flow_id": "pcf_a1b2c3d4e5f6"
},
"meta": {
"requestId": "preq_0a9b8c7d6e5f4a31"
}
}
See the code exchange example. Pickup repeats return the same code while it remains valid and unexchanged; token exchange is single-use.
Errors
| Status | Error | Meaning |
|---|---|---|
| 400 | validation_failed | Invalid path, unsupported query parameter or unexpected body |
| 401 | unauthorized | Invalid partner token |
| 403 | insufficient_scope, partner_not_enabled, partner_past_due, partner_suspended, partner_disabled, partner_not_active | Scope or service policy blocks code pickup |
| 404 | not_found | Flow is unknown, belongs to another member/client, or uses redirect delivery |
| 410 | flow_closed | Flow/code expired, was denied, revoked or superseded; obtain a new authorization link |
| 429 | rate_limited, too_many_concurrent_requests | Honor Retry-After |
See Partner errors for common availability errors and response shapes.