REST API Reference
| Field | Value |
|---|---|
| Document ID | ASCEND-SDK-012 |
| Version | 2026.05 |
| Last Updated | April 2026 |
| Author | Ascend Engineering Team |
| Publisher | OW-KAI Technologies Inc. |
| Classification | Enterprise Client Documentation |
| Compliance | SOC 2 CC6.1/CC6.2, PCI-DSS 7.1/8.3, HIPAA 164.312, NIST 800-53 AC-2/SI-4 |
Reading Time: 15 minutes | Skill Level: Intermediate
Overview
The ASCEND REST API allows direct HTTP integration from any programming language or platform. All endpoints use JSON request/response bodies and require authentication.
Store API keys in environment variables, never in source code or version control. Include your key in the Authorization: Bearer header on every request.
Base URL
https://pilot.owkai.app
Authentication
All requests require an API key via one of these methods:
Bearer Token (Recommended)
curl -H "Authorization: Bearer owkai_your_key_here" \
https://pilot.owkai.app/api/v1/actions/submit
X-API-Key Header
curl -H "X-API-Key: owkai_your_key_here" \
https://pilot.owkai.app/api/v1/actions/submit
Both Headers (Enterprise)
For banking-level security, include both:
curl -H "Authorization: Bearer owkai_your_key_here" \
-H "X-API-Key: owkai_your_key_here" \
https://pilot.owkai.app/api/v1/actions/submit
Authentication Requirements Per Endpoint
| Endpoint | Method | Auth |
|---|---|---|
/api/v1/actions/submit | POST | API key or Bearer |
/api/sdk/kill-switch/status | GET | API key or Bearer |
/api/audit/logs | GET | API key or Bearer |
/api/governance/workflows/{id}/approve | POST | Cognito JWT only |
/api/governance/workflows/{id}/deny | POST | Cognito JWT only |
Approval and denial endpoints require a Cognito JWT (user session token). API keys are rejected with HTTP 401. Agents cannot approve their own actions — this separation is enforced at the API layer.
Common Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <api_key> |
Content-Type | Yes | application/json |
X-API-Key | Optional | Alternate authentication |
X-Correlation-ID | Optional | Request tracing ID |
X-Request-Timestamp | Optional | ISO 8601 timestamp |
Action Endpoints
Submit Action
Submit an agent action for governance evaluation.
Endpoint: POST /api/v1/actions/submit
Request Body:
{
"agent_id": "my-agent-001",
"agent_name": "My AI Agent",
"action_type": "database_read",
"description": "Read customer data for report",
"tool_name": "postgresql",
"resource_id": "customers_table",
"action_details": {
"table": "customers",
"operation": "SELECT",
"columns": ["id", "name", "email"]
},
"context": {
"session_id": "sess_abc123",
"environment": "production"
},
"risk_indicators": {
"data_classification": "pii"
}
}
Required Fields:
| Field | Type | Description |
|---|---|---|
agent_id | string | Unique agent identifier |
agent_name | string | Human-readable agent name |
action_type | string | Action category |
description | string | What the action does |
tool_name | string | Tool/service being used |
Optional Fields:
| Field | Type | Description |
|---|---|---|
resource_id | string | Target resource identifier |
action_details | object | Action-specific parameters |
context | object | Execution context |
risk_indicators | object | Pre-computed risk signals |
Optional Governance Fields
mcp_server_name (string, optional)
The registered MCP server name for this tool call. Triggers Layer 13 MCP governance enforcement (G-P0-01). The server must be registered and active in Agent Registry → MCP Servers.
mcp_server_name is matched exactly as registered. "my-server" and "My-Server" are treated as different registrations. Use the exact string from your MCP server registration.
Error responses when server is not registered:
{
"detail": {
"error": "MCP server governance violation",
"detail": "MCP server is not registered. Register at Agent Registry → MCP Servers.",
"mcp_server_name": "your-server-name",
"correlation_id": "action_20260504_..."
}
}
Register an MCP server: POST /api/registry/mcp-servers
model_id (string, optional)
The model identifier from your organization's Model Registry. Triggers model compliance enforcement (G-P0-02) per SR-11-7 and EU AI Act Art. 9.
The model_id is the string identifier you assigned when registering the model — not the database ID. Example: "gpt-4-turbo-2024-04-09".
If you omit model_id, ASCEND checks whether the submitting agent has a linked model (configured in Agent Registry). If a linked model exists, it is used automatically. If not, the action proceeds without model governance.
Error responses when model is not registered:
{
"detail": {
"error": "Model governance violation",
"detail": "Model is not registered in the organization model registry.",
"model_id": "your-model-id",
"correlation_id": "action_20260504_..."
}
}
Register a model: Model Registry in the ASCEND dashboard. To list registered models via API, a Cognito JWT session token is required (use the dashboard to discover model identifiers).
Response (200 OK):
{
"id": 12345,
"action_id": "act_abc123xyz",
"status": "approved",
"risk_score": 3.5,
"risk_level": "low",
"summary": "Action approved - low risk database read",
"created_at": "2025-12-16T10:30:00Z",
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"nist_controls": ["AC-3", "AU-12"],
"mitre_techniques": []
}
Response Fields:
| Field | Type | Description |
|---|---|---|
id | integer | Numeric action ID |
action_id | string | String action ID |
status | string | approved, denied, pending |
risk_score | float | Risk score (0-100) |
risk_level | string | low, medium, high, critical |
summary | string | Decision explanation |
denial_reason | string | Reason if denied |
pending_approvers | array | Approvers if pending |
Example:
curl -X POST https://pilot.owkai.app/api/v1/actions/submit \
-H "Authorization: Bearer owkai_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-agent",
"agent_name": "My Agent",
"action_type": "database_read",
"description": "Query customers",
"tool_name": "postgresql"
}'
Get Action Status
Check the status of a submitted action.
Endpoint: GET /api/v1/actions/{action_id}/status
Response (200 OK):
{
"id": 12345,
"status": "approved",
"risk_score": 3.5,
"risk_level": "low",
"updated_at": "2025-12-16T10:30:00Z"
}
Example:
curl https://pilot.owkai.app/api/v1/actions/12345/status \
-H "Authorization: Bearer owkai_your_key_here"
Get Action Details
Get full action details including audit trail.
Endpoint: GET /api/v1/actions/{action_id}
Response (200 OK):
{
"id": 12345,
"agent_id": "my-agent-001",
"agent_name": "My AI Agent",
"action_type": "database_read",
"description": "Query customers",
"status": "approved",
"risk_score": 3.5,
"risk_level": "low",
"created_at": "2025-12-16T10:30:00Z",
"audit_trail": [
{
"timestamp": "2025-12-16T10:30:00Z",
"event": "submitted",
"actor": "agent"
},
{
"timestamp": "2025-12-16T10:30:01Z",
"event": "approved",
"actor": "auto"
}
]
}
List Actions
List recent actions with optional filtering.
Endpoint: GET /api/v1/actions
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | int | 50 | Max results (1-100) |
offset | int | 0 | Pagination offset |
status | string | - | Filter by status |
agent_id | string | - | Filter by agent |
Response (200 OK):
{
"actions": [
{
"id": 12345,
"agent_id": "my-agent",
"action_type": "database_read",
"status": "approved",
"risk_level": "low",
"created_at": "2025-12-16T10:30:00Z"
}
],
"total": 150,
"limit": 50,
"offset": 0,
"has_more": true
}
Example:
curl "https://pilot.owkai.app/api/v1/actions?limit=10&status=pending" \
-H "Authorization: Bearer owkai_your_key_here"
Agent Endpoints
Register Agent
Register a new agent with ASCEND.
Endpoint: POST /api/registry/agents
Request Body:
{
"agent_id": "my-agent-001",
"display_name": "My AI Agent",
"agent_type": "supervised",
"environment": "production",
"capabilities": ["data_access", "file_operations"],
"allowed_resources": ["production_db"],
"metadata": {
"version": "1.0.0",
"team": "data-engineering"
}
}
Response (201 Created):
{
"agent_id": "my-agent-001",
"status": "active",
"trust_level": "standard",
"created_at": "2025-12-16T10:30:00Z"
}
Get Agent Status
Endpoint: GET /api/registry/agents/{agent_id}
Response (200 OK):
{
"agent_id": "my-agent-001",
"display_name": "My AI Agent",
"status": "active",
"trust_level": "standard",
"last_activity": "2025-12-16T10:30:00Z",
"action_count": 150,
"denial_count": 5
}
Approval Endpoints
Check Approval Status
Endpoint: GET /api/sdk/approval/{approval_id}
Response (200 OK):
{
"approval_id": "apr_abc123",
"status": "approved",
"approved_by": "admin@company.com",
"decided_at": "2025-12-16T10:35:00Z",
"comments": "Approved for production deployment"
}
Approve Action (Admin)
Endpoint: POST /api/actions/{action_id}/approve
Request Body:
{
"comments": "Approved after security review"
}
Response (200 OK):
{
"status": "approved",
"approved_by": "admin@company.com",
"approved_at": "2025-12-16T10:35:00Z"
}
Health & Info
Health Check
Endpoint: GET /health
Response (200 OK):
{
"status": "healthy",
"timestamp": "2025-12-16T10:30:00Z"
}
Deployment Info
Endpoint: GET /api/deployment-info
Response (200 OK):
{
"version": "2.5.0",
"environment": "production",
"region": "us-east-2",
"features": ["smart_rules", "mcp_governance", "byok"]
}
Kill-Switch Status
Check whether your organization's kill-switch is currently active. The ASCEND SDK polls this endpoint automatically. REST API callers must implement polling manually to receive kill-switch signals.
Endpoint: GET /api/sdk/kill-switch/status
Auth: API key or Bearer token
Response when inactive:
{"blocked": false, "reason": null}
Response when active:
{"blocked": true, "reason": "Kill-switch activated"}
If this endpoint is unreachable, treat the response as blocked: true. Never default to blocked: false on a connection error. See Fail-Closed Reference Implementations below.
Error Responses
Error Format
All errors return JSON with this structure:
{
"detail": "Error message here",
"error_code": "ERROR_CODE",
"status_code": 400
}
HTTP Status Codes
| Code | Meaning | Common Causes |
|---|---|---|
| 200 | Success | Request completed |
| 201 | Created | Resource created |
| 400 | Bad Request | Invalid JSON, missing fields |
| 401 | Unauthorized | Invalid API key |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Duplicate resource |
| 422 | Unprocessable | Validation failed |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | Internal error |
Error Codes
| Code | Description |
|---|---|
INVALID_API_KEY | API key is invalid or expired |
MISSING_REQUIRED_FIELD | Required field not provided |
INVALID_ACTION_TYPE | Unrecognized action type |
AGENT_NOT_FOUND | Agent ID not registered |
RATE_LIMIT_EXCEEDED | Too many requests |
POLICY_VIOLATION | Action violates policy |
Response Body Schema
All governance decisions are returned in the response body. Extract decision context from these fields:
| Field | Type | Description |
|---|---|---|
id | integer | Internal action record ID |
action_id | string | Unique action identifier |
status | string | approved, pending_approval, denied |
risk_score | float | Composite risk score (0–100) |
risk_level | string | low, medium, high, critical |
requires_approval | boolean | Whether human review is pending |
correlation_id | string | Use for support tickets and audit correlation |
compliance_mapping | object | NIST, MITRE ATT&CK framework mappings |
message | string | Human-readable decision summary |
If you need decision context as HTTP response headers (X-Ascend-Action-Id, X-Ascend-Risk-Score, etc.) rather than body fields, use the Envoy/Istio, Lambda Authorizer, or Kong gateway integration paths. Those paths inject governance metadata as headers automatically — no body parsing required.
Rate Limits
| Tier | Requests/Minute | Requests/Hour |
|---|---|---|
| Free | 60 | 1,000 |
| Pro | 600 | 10,000 |
| Enterprise | 6,000 | Unlimited |
Rate limit headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1702725600
Retry-After: 30
Webhooks
Configure Webhook
Endpoint: POST /api/sdk/webhooks/configure
Request Body:
{
"url": "https://your-app.com/webhooks/ascend",
"events": ["action.approved", "action.denied", "policy.violation"],
"secret": "whsec_your_secret_here"
}
Webhook Payload:
{
"event": "action.approved",
"timestamp": "2025-12-16T10:30:00Z",
"data": {
"action_id": "act_abc123",
"agent_id": "my-agent-001",
"risk_score": 3.5
},
"signature": "v1=abc123..."
}
Complete Example
import requests
import os
BASE_URL = "https://pilot.owkai.app"
API_KEY = os.environ["ASCEND_API_KEY"]
def evaluate_action(action_type, description, tool_name, **kwargs):
"""Evaluate an action for governance."""
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
payload = {
"agent_id": "my-agent-001",
"agent_name": "My AI Agent",
"action_type": action_type,
"description": description,
"tool_name": tool_name,
**kwargs
}
response = requests.post(
f"{BASE_URL}/api/v1/actions/submit",
headers=headers,
json=payload,
timeout=30
)
if response.status_code == 200:
result = response.json()
return result
elif response.status_code == 401:
raise Exception("Invalid API key")
elif response.status_code == 422:
raise Exception(f"Validation error: {response.json()}")
else:
raise Exception(f"API error: {response.status_code}")
# Usage
result = evaluate_action(
action_type="database_read",
description="Query customer data",
tool_name="postgresql",
action_details={"table": "customers"}
)
if result["status"] == "approved":
print(f"Approved! Action ID: {result['id']}")
else:
print(f"Status: {result['status']}")
Fail-Closed Reference Implementations
Banking-grade callers must wrap every governed action with these four behaviors:
- Kill-switch pre-check — call
GET /api/sdk/kill-switch/statusbefore every action; treat any non-200 or connection error asblocked: true. - Fail-closed on ASCEND unreachable — never proceed when the platform is unreachable.
- Exponential backoff with jitter — retry HTTP 429 and 503 with
2^attemptseconds + up to 25% jitter; cap at 5 attempts. - HTTP 402 treated as kill-switch active — backend returns 402 when an org's kill-switch or spend limit is engaged; treat identically to a
blocked: truestatus response.
The Python quick-start in Complete Example above is for prototype use. The implementations below are the banking-grade reference for production REST callers.
Python
import os
import random
import time
import requests
BASE_URL = "https://pilot.owkai.app"
API_KEY = os.environ["ASCEND_API_KEY"]
TIMEOUT = 30
MAX_RETRIES = 5
class AscendBlocked(Exception):
"""Raised when ASCEND blocks the action — fail-closed."""
def _sleep_with_jitter(attempt: int) -> None:
base = 2 ** attempt # 1s, 2s, 4s, 8s, 16s
delay = base + random.uniform(0, 0.25 * base)
time.sleep(delay)
def _is_kill_switch_active() -> bool:
"""Fail-closed: any error treats kill-switch as ACTIVE."""
try:
r = requests.get(
f"{BASE_URL}/api/sdk/kill-switch/status",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=5,
)
if r.status_code != 200:
return True
return bool(r.json().get("blocked"))
except Exception:
return True
def evaluate_action_governed(payload: dict) -> dict:
if _is_kill_switch_active():
raise AscendBlocked("kill-switch active or status endpoint unreachable")
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
for attempt in range(MAX_RETRIES):
try:
r = requests.post(
f"{BASE_URL}/api/v1/actions/submit",
headers=headers,
json=payload,
timeout=TIMEOUT,
)
except requests.RequestException:
raise AscendBlocked("ASCEND unreachable")
if r.status_code == 402:
raise AscendBlocked("kill-switch / spend limit active")
if r.status_code in (429, 503):
if attempt == MAX_RETRIES - 1:
raise AscendBlocked(f"max retries exceeded ({r.status_code})")
_sleep_with_jitter(attempt)
continue
if r.status_code == 200:
return r.json()
raise AscendBlocked(f"unexpected status {r.status_code}")
raise AscendBlocked("max retries exceeded")
Node.js
const BASE_URL = 'https://pilot.owkai.app';
const API_KEY = process.env.ASCEND_API_KEY;
const TIMEOUT_MS = 30_000;
const MAX_RETRIES = 5;
class AscendBlocked extends Error {
constructor(reason) {
super(`ASCEND blocked: ${reason}`);
this.name = 'AscendBlocked';
}
}
function sleepWithJitter(attempt) {
const base = 1000 * 2 ** attempt; // 1s, 2s, 4s, 8s, 16s
const delay = base + Math.random() * 0.25 * base;
return new Promise((resolve) => setTimeout(resolve, delay));
}
async function isKillSwitchActive() {
// Fail-closed: any error treats kill-switch as ACTIVE.
try {
const r = await fetch(`${BASE_URL}/api/sdk/kill-switch/status`, {
headers: { Authorization: `Bearer ${API_KEY}` },
signal: AbortSignal.timeout(5_000),
});
if (!r.ok) return true;
const body = await r.json();
return Boolean(body.blocked);
} catch {
return true;
}
}
async function submitActionGoverned(payload) {
if (await isKillSwitchActive()) {
throw new AscendBlocked('kill-switch active or status endpoint unreachable');
}
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
let r;
try {
r = await fetch(`${BASE_URL}/api/v1/actions/submit`, {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
signal: AbortSignal.timeout(TIMEOUT_MS),
});
} catch {
throw new AscendBlocked('ASCEND unreachable');
}
if (r.status === 402) throw new AscendBlocked('kill-switch / spend limit active');
if (r.status === 429 || r.status === 503) {
if (attempt === MAX_RETRIES - 1) {
throw new AscendBlocked(`max retries exceeded (${r.status})`);
}
await sleepWithJitter(attempt);
continue;
}
if (r.status === 200) return await r.json();
throw new AscendBlocked(`unexpected status ${r.status}`);
}
throw new AscendBlocked('max retries exceeded');
}
Go
package ascend
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"math/rand"
"net/http"
"os"
"time"
)
const (
BaseURL = "https://pilot.owkai.app"
Timeout = 30 * time.Second
MaxRetries = 5
)
var ErrAscendBlocked = errors.New("ascend blocked")
func sleepWithJitter(ctx context.Context, attempt int) error {
base := time.Duration(1<<attempt) * time.Second // 1s, 2s, 4s, 8s, 16s
jitter := time.Duration(rand.Float64() * float64(base) * 0.25)
select {
case <-time.After(base + jitter):
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func isKillSwitchActive(ctx context.Context, client *http.Client) bool {
// Fail-closed: any error treats kill-switch as ACTIVE.
req, _ := http.NewRequestWithContext(ctx, "GET",
BaseURL+"/api/sdk/kill-switch/status", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("ASCEND_API_KEY"))
r, err := client.Do(req)
if err != nil {
return true
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return true
}
var body struct {
Blocked bool `json:"blocked"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
return true
}
return body.Blocked
}
func SubmitActionGoverned(ctx context.Context, payload map[string]any) (map[string]any, error) {
client := &http.Client{Timeout: Timeout}
if isKillSwitchActive(ctx, client) {
return nil, fmt.Errorf("%w: kill-switch active or status endpoint unreachable", ErrAscendBlocked)
}
body, _ := json.Marshal(payload)
for attempt := 0; attempt < MaxRetries; attempt++ {
req, err := http.NewRequestWithContext(ctx, "POST",
BaseURL+"/api/v1/actions/submit", bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrAscendBlocked, err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("ASCEND_API_KEY"))
req.Header.Set("Content-Type", "application/json")
r, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("%w: ASCEND unreachable", ErrAscendBlocked)
}
defer r.Body.Close()
switch {
case r.StatusCode == http.StatusPaymentRequired: // 402
return nil, fmt.Errorf("%w: kill-switch / spend limit active", ErrAscendBlocked)
case r.StatusCode == http.StatusTooManyRequests || r.StatusCode == http.StatusServiceUnavailable:
if attempt == MaxRetries-1 {
return nil, fmt.Errorf("%w: max retries exceeded (%d)", ErrAscendBlocked, r.StatusCode)
}
if err := sleepWithJitter(ctx, attempt); err != nil {
return nil, err
}
continue
case r.StatusCode == http.StatusOK:
var result map[string]any
if err := json.NewDecoder(r.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("%w: invalid response", ErrAscendBlocked)
}
return result, nil
default:
return nil, fmt.Errorf("%w: unexpected status %d", ErrAscendBlocked, r.StatusCode)
}
}
return nil, fmt.Errorf("%w: max retries exceeded", ErrAscendBlocked)
}
Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
public class AscendClient {
private static final String BASE_URL = "https://pilot.owkai.app";
private static final Duration TIMEOUT = Duration.ofSeconds(30);
private static final int MAX_RETRIES = 5;
private final String apiKey = System.getenv("ASCEND_API_KEY");
private final HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
public static class AscendBlocked extends RuntimeException {
public AscendBlocked(String reason) { super("ASCEND blocked: " + reason); }
}
private void sleepWithJitter(int attempt) throws InterruptedException {
long base = (1L << attempt) * 1000L; // 1s, 2s, 4s, 8s, 16s
long jitter = (long) (ThreadLocalRandom.current().nextDouble() * base * 0.25);
Thread.sleep(base + jitter);
}
private boolean isKillSwitchActive() {
// Fail-closed: any error treats kill-switch as ACTIVE.
try {
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/api/sdk/kill-switch/status"))
.header("Authorization", "Bearer " + apiKey)
.timeout(Duration.ofSeconds(5))
.GET().build();
HttpResponse<String> r = http.send(req, HttpResponse.BodyHandlers.ofString());
if (r.statusCode() != 200) return true;
return r.body().contains("\"blocked\":true");
} catch (Exception e) {
return true;
}
}
public Map<String, Object> submitActionGoverned(String jsonPayload) throws InterruptedException {
if (isKillSwitchActive()) {
throw new AscendBlocked("kill-switch active or status endpoint unreachable");
}
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/api/v1/actions/submit"))
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.timeout(TIMEOUT)
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
.build();
HttpResponse<String> r;
try {
r = http.send(req, HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
throw new AscendBlocked("ASCEND unreachable");
}
int code = r.statusCode();
if (code == 402) throw new AscendBlocked("kill-switch / spend limit active");
if (code == 429 || code == 503) {
if (attempt == MAX_RETRIES - 1) throw new AscendBlocked("max retries exceeded (" + code + ")");
sleepWithJitter(attempt);
continue;
}
if (code == 200) {
// Parse JSON via your preferred library (Jackson, Gson, etc.)
return Map.of("body", r.body());
}
throw new AscendBlocked("unexpected status " + code);
}
throw new AscendBlocked("max retries exceeded");
}
}
Next Steps
- Python SDK — Python SDK wrapper
- Node.js SDK — Node.js SDK wrapper
- Gateway Overview — API Gateway integration
Document Version: 2026.05 | Last Updated: May 2026