Processes a customer's USSD request
const url = 'https://api.pipevest.com/v1/ussd/process';const options = { method: 'POST', headers: { 'X-Idempotency-Key': '2489E9AD-2EE2-8E00-8EC9-32D5F69181C0', Authorization: 'Bearer <token>', 'x-client-id': '<x-client-id>', 'Content-Type': 'application/json' }, body: '{"sessionId":"ussd_3278668b308502f3a562950fc8043028","choice":"1"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.pipevest.com/v1/ussd/process \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --header 'X-Idempotency-Key: 2489E9AD-2EE2-8E00-8EC9-32D5F69181C0' \ --header 'x-client-id: <x-client-id>' \ --data '{ "sessionId": "ussd_3278668b308502f3a562950fc8043028", "choice": "1" }'Advances an existing session. The body is { sessionId, choice } — it
does not take a customerId.
What the customer sees (example: the Stocks menu):
Select Stock: 1. AAPL 2. AMZN 7. Next 8. Search 0. Home
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Idempotency key (UUID) scoped to your credential. Mandatory on both
USSD endpoints in Sandbox (UAT) and Production alike — a request without
one is rejected with 400. Retrying with the same key replays the stored
response verbatim rather than re-running the operation.
Request Bodyrequired
Section titled “Request Bodyrequired”The session and the customer’s menu choice.
object
Unique session identifier.
Example
ussd_3278668b308502f3a562950fc8043028The customer’s menu choice, always sent as a string. May be a numeric key like “1” or a free-text search term like “Tesla”. Alphanumeric, 1–10 characters.
Example
1Responses
Section titled “Responses”USSD request processed. Two bodies share this status.
On the normal path message is "USSD request processed successfully". On an unexpected internal failure the session is
moved to ERROR and message becomes "USSD request processed with error state" — data is the SAME ProcessResponse shape either
way, with currentState and menu.state set to "ERROR". Detect
the error state on the payload, not on the status code.
object
object
The USSD process request response data.
object
Unique session identifier.
The menu structure with state, text, options, and end state flag.
object
A menu option with key, label, optional next state, and optional data.
object
The option key (e.g. “1”, “2”, or a search term).
The display label for the option.
The next state to transition to (some options, like “Next”, omit this).
An operation the option performs rather than a plain navigation.
Mutually exclusive with nextState; most options carry neither.
Today the only option that carries it is “Yes, cancel” in the
CANCEL_REQUEST_CONFIRMATION menu.
Optional additional data associated with the option (e.g. stock information).
object
Examples
Stocks menu response
{ "message": "USSD request processed successfully", "data": { "sessionId": "ussd_3278668b308502f3a562950fc8043028", "currentState": "STOCKS", "menu": { "state": "STOCKS", "text": "Select Stock:", "options": [ { "key": "1", "label": "AAPL", "nextState": "SELECT_STOCK", "data": { "stockId": "cmhkp4jkw0005zxfok9m0kiwg", "symbol": "AAPL", "name": "Apple Inc." } }, { "key": "2", "label": "AMZN", "nextState": "SELECT_STOCK", "data": { "stockId": "cmhkp4kcp0008zxfohlxjtvd7", "symbol": "AMZN", "name": "Amazon.com Inc." } }, { "key": "7", "label": "Next" }, { "key": "8", "label": "Search", "nextState": "SEARCH_STOCK" }, { "key": "0", "label": "Home", "nextState": "HOME" } ], "isEndState": false }, "ussdText": "Select Stock:\n\n1. AAPL\n2. AMZN\n7. Next\n8. Search\n0. Home", "expiresAt": "2025-11-06T14:38:12.202Z" }}Error-state response (still HTTP 200)
{ "message": "USSD request processed with error state", "data": { "sessionId": "ussd_3278668b308502f3a562950fc8043028", "currentState": "ERROR", "menu": { "state": "ERROR", "text": "Sorry, something went wrong. Please try again later.", "options": [ { "key": "0", "label": "Home", "nextState": "HOME" } ], "isEndState": false }, "ussdText": "Sorry, something went wrong. Please try again later.\n\n0. Home", "expiresAt": "2025-11-06T14:38:12.202Z" }}Request failed schema validation, the session is expired, the session
is no longer active, or the mandatory X-Idempotency-Key header is
missing.
object
object
object
Examples
{ "error": "Validation failed", "details": [ { "field": "choice", "message": "Choice must be alphanumeric" } ]}{ "error": "SESSION_EXPIRED", "message": "Session has expired"}{ "error": "Bad Request", "message": "X-Idempotency-Key header is required for this request"}Missing, invalid, expired or revoked token; x-client-id missing or not
matching the token; or the request signature / Content-Digest failed
verification.
object
Example
{ "error": "Unauthorized", "message": "Organization authentication token is required"}Either the session belongs to another organization, or the client IP is outside the credential’s allowlist.
object
Example
{ "error": "SESSION_FORBIDDEN", "message": "Session does not belong to this organization"}No session with that sessionId
object
Example
{ "error": "SESSION_NOT_FOUND", "message": "USSD session not found"}A request with this X-Idempotency-Key is still in progress
object
Example
{ "error": "Conflict", "message": "A request with this X-Idempotency-Key is already in progress"}The X-Idempotency-Key was already used with a different body
object
Example
{ "error": "Unprocessable Entity", "message": "X-Idempotency-Key was already used with a different request payload"}Rate limit exceeded
object
Example
{ "error": "Too Many Requests", "message": "Too many requests. Please slow down and try again shortly."}