Reference
Hosted API
Base URL
All calls use this origin and the /api/v1 prefix.
https://app.sreverse.simpalabs.com/api/v1Authentication
Send your key as a bearer token on every call. There is no session cookie and no signed request to build yourself.
Authorization: Bearer srv_1a2b3c4d5e6f.<secret>
Keys are issued once, when the project is delivered, and shown a single time. The value above is a placeholder: replace srv_1a2b3c4d5e6f and <secret> with the key you were given. The account that owns the project can issue a replacement key, and revoke a key, from the project page.
The platform also accepts the key in an x-api-key header. Requests are authenticated by the header alone, so any origin may call the API and no ambient cookie credential is exposed.
Discovery
GET /api/v1 lists the endpoints your key can call, the connector version behind them, and recent usage. Start here rather than guessing at endpoint names.
The discovery call is the same URL as the base URL. It returns the endpoints available to that key, the connector version, and the ten most recent calls on the project.
curl -sS "https://app.sreverse.simpalabs.com/api/v1" \ -H "Authorization: Bearer srv_1a2b3c4d5e6f.<secret>"
The response looks like this.
{
"project": { "code": "SRV-2026-0001", "app": "Example app", "status": "delivered" },
"connector": {
"title": "Example app mobile API",
"version": 2,
"activatedAt": "2026-02-04T10:15:00.000Z",
"signsRequests": true,
"encryptsRequests": false
},
"key": { "label": "Default key", "callCount": 128 },
"endpoints": [
{
"name": "lookup",
"method": "POST",
"path": "/v2/reference/lookup",
"description": "Look up a reference and return its status.",
"anonymous": false,
"input": [
{
"name": "reference",
"type": "string",
"required": true,
"description": "The reference to look up."
}
]
}
],
"usage": [
{
"endpoint": "lookup",
"status": 200,
"upstreamStatus": 200,
"latencyMs": 412,
"at": "2026-02-04T10:31:07.000Z"
}
],
"call": "https://app.sreverse.simpalabs.com/api/v1/<endpoint>"
}The call field shows the URL shape for executing an endpoint. The input array on each endpoint lists the fields that endpoint accepts, which is what you send in the request body. A key with no active connector returns no_active_endpoint.
Calling an endpoint
POST /api/v1/<endpoint> executes one recovered workflow and returns its result.
The JSON request body supplies the input fields that discovery declared for the endpoint. Query string parameters fill in anything the body omits. Send POST for every endpoint; GET also works and takes its input from the query string only, which is convenient for a check from a browser. The method the upstream expects is decided by the connector, not by how you call the platform.
POST /api/v1/lookup
Content-Type: application/json
{"reference": "ABC123"}GET /api/v1/lookup?reference=ABC123
The platform runs the whole recovered flow on your behalf: the login, the request signing, any payload encryption, and the decryption of the response. You send plain JSON and receive the decoded body, or the single field the connector selects from it.
Examples
Three complete calls
Each sample calls an endpoint named lookup with a body of {"reference": "ABC123"}.
curl
curl -sS -X POST "https://app.sreverse.simpalabs.com/api/v1/lookup" \
-H "Authorization: Bearer srv_1a2b3c4d5e6f.<secret>" \
-H "Content-Type: application/json" \
-d '{"reference": "ABC123"}'Python with requests
import requests
BASE_URL = "https://app.sreverse.simpalabs.com/api/v1"
API_KEY = "srv_1a2b3c4d5e6f.<secret>"
response = requests.post(
BASE_URL + "/lookup",
headers={
"Authorization": "Bearer " + API_KEY,
"Content-Type": "application/json",
},
json={"reference": "ABC123"},
timeout=30,
)
print("latency:", response.headers.get("x-sreverse-latency-ms"))
if response.ok:
print(response.json())
else:
failure = response.json()
print(failure["error"]["code"], failure["error"]["message"])
for line in failure.get("trace", []):
print(line)TypeScript with fetch
const BASE_URL = "https://app.sreverse.simpalabs.com/api/v1";
const API_KEY = "srv_1a2b3c4d5e6f.<secret>";
const response = await fetch(BASE_URL + "/lookup", {
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ reference: "ABC123" }),
});
const latencyMs = response.headers.get("x-sreverse-latency-ms");
if (!response.ok) {
const failure = await response.json();
throw new Error(failure.error.code + ": " + failure.error.message);
}
const result = await response.json();
console.log(result, latencyMs);Reading the response
- A successful call returns HTTP 200 and the decoded body as JSON. If the connector selects a single field from the upstream response, that value is returned on its own.
- The metadata you usually want is in the response headers, not the body: the endpoint name, the latency, the upstream status and the connector id.
- A failed call returns the status from the error table below and a body with an error object. Check error.code rather than the message, which is written for a person and may change.
Response headers
These are present on calls that reached the connector. A request rejected before that, such as one with an invalid key or malformed JSON, returns only the CORS headers.
| Header | Meaning |
|---|---|
| x-sreverse-endpoint | The endpoint name the call ran against. |
| x-sreverse-latency-ms | Total milliseconds the platform spent on the call, including the upstream round trip. |
| x-sreverse-upstream-status | The HTTP status the upstream returned. Absent when the call never reached it. |
| x-sreverse-connector | The id of the connector version that handled the call. Quote it when reporting a problem. |
Errors
Failures return an error object and a trace array describing what the connector did.
{
"error": {
"code": "upstream_error",
"message": "The upstream service returned 503.",
"upstreamStatus": 503
},
"trace": [
"session established with 2 captured value(s)",
"POST https://api.example.com/v2/reference/lookup -> 503 in 388ms",
"failed: The upstream service returned 503."
]
}The trace is written to be safe to share. It lists the steps the connector took, redacted of secrets and credentials, and it is the first thing to include when reporting a problem.
| Code | HTTP status | Meaning |
|---|---|---|
| missing_key | 401 | No Authorization bearer header or x-api-key was sent. |
| invalid_key | 401 | The key matches no issued key, or it has been revoked. |
| no_active_endpoint | 409 | The project has no active connector, for example before delivery or while a connector is disabled. |
| invalid_json | 400 | The request body was not valid JSON. |
| unknown_endpoint | 404 | The connector has no endpoint with that name. |
| upstream_error | 502 | The upstream returned an error status. Its status is in error.upstreamStatus and in the x-sreverse-upstream-status header. |
| upstream_timeout | 502 | The upstream did not answer within the connector timeout, which is 30 seconds by default. |
| login_failed | 502 | The connector login call returned an error status. |
| session_extract_failed | 502 | Login succeeded but a session value mapped from the response was absent. |
| decrypt_failed | 502 | An encrypted upstream response could not be decrypted with the configured key. |
| template_missing_value | 502 | A request template referenced an input field or secret that was not supplied. |
| invalid_key_length | 502 | The configured signing or encryption key did not decode to the length the algorithm requires. |
Guarantees
- Upstream credentials stay server side. The credentials recovered during the work, including signing keys and account passwords, are held by the platform and used only to make your calls. They are never sent to your application or your browser.
- Your key only ever reaches the platform. It authenticates you to the hosted endpoint. Everything the upstream expects, including signatures and encrypted payloads, is built on the platform.
- Every call is metered. Each call is recorded with its endpoint, status, upstream status and latency. The discovery response reports the call count for the key and the ten most recent calls on the project.
- Keys can be revoked at any time. Revoke a key from the project page and it stops working immediately, after which it returns invalid_key with HTTP 401.