Simulix

API

Simulations

Run a calibrated workflow against synthetic agents and retrieve per-agent responses. All requests are async per ADR-006 — POST returns 202 immediately; results arrive via webhook (recommended) or polling.

Overview

A simulation runs a calibrated workflow (e.g.

cdc_nhis_smoking
) against the workflow's target demographics and returns per-agent reactions. The 76 currently-published workflows are listed in the API reference.

POST returns immediately with a simulation id. Results land either via webhook (push, recommended) or polling (pull, fallback).

Authentication

Bearer token via the

Authorization
header. POST requires
simulations:write
; the three GET endpoints require
simulations:read
. See /docs/api-keys for key management.

POST /v1/simulations

Queue a simulation. Returns 202 with a poll URL and (on the first webhook-enabled POST per organization) a

 webhook_signing_secret
.

Request body

{
  "workflow_id": "cdc_nhis_smoking",
  "agent_count": 200,
  "scenario_context": { "region": "US" },
  "callback_url": "https://your-app.com/webhooks/simulix"
}

Response — 202

{
  "data": {
    "id": "0190a8b4-1c34-7d2a-9e87-2c4f3b5a6d8e",
    "status": "queued",
    "created_at": "2026-05-02T14:30:00Z",
    "estimated_completion_time_seconds": 40,
    "poll_url": "https://api.simulix.com/v1/simulations/0190a8b4-...",
    "webhook_subscribed": true,
    "webhook_signing_secret": "ZWZxYjg4...REDACTED-CAPTURE-NOW"
  },
  "error": null,
  "meta": { "correlation_id": "01HX..." }
}

Save your webhook signing secret immediately. Your webhook signing secret appears in this response only once, on your first webhook-enabled simulation request. Save it immediately — Simulix does not display it again. If lost, contact support@simulix.com for regeneration. Self-serve rotation API is on the roadmap.

GET /v1/simulations

Cursor-paginated list of your organization's simulations. Query params:

cursor
(last-seen id, optional),
limit
(1–100, default 50). Response
meta
carries
next_cursor
+
has_more
.

GET /v1/simulations/{id}

Status polling endpoint. While

queued
or
running
, sets a
Retry-After: 5
header so SDKs back off politely.
results_available=true
signals that
GET /v1/simulations/{id}/results
will return 200.

GET /v1/simulations/{id}/results

Returns full per-agent results when status is

completed
or
failed
. Returns
409 simulation_not_complete
while still in flight — keep polling the status endpoint until
results_available=true
.

Webhook delivery

When you supply

callback_url
on POST, Simulix delivers
simulation.completed
or
simulation.failed
events to that URL on terminal state. Up to 5 attempts over ~14.6 hours on non-2xx responses (backoff: 1m, 5m, 30m, 2h, 12h).

Payload shape

POST https://your-app.com/webhooks/simulix
Content-Type: application/json
X-Simulix-Signature: t=1714000000,v1=<hmac-sha256-hex>
X-Simulix-Event: simulation.completed
X-Simulix-Delivery-Id: 0190ab4f-...

{
  "id": "evt_a1b2c3d4e5f6...",
  "type": "simulation.completed",
  "created": 1714000000,
  "data": {
    "simulation_id": "0190a8b4-1c34-7d2a-9e87-2c4f3b5a6d8e",
    "workflow_id": "cdc_nhis_smoking",
    "status": "completed",
    "results": {
      "agent_count": 200,
      "rounds": 2,
      "agent_responses": [ ... ]
    }
  }
}

Verifying the signature (Python)

import hashlib, hmac, time

def verify_simulix_webhook(
    payload: bytes,
    signature_header: str,
    secret: str,
    tolerance_seconds: int = 300,
) -> bool:
    # Header shape: "t=<unix>,v1=<hex>"
    parts = dict(p.split("=", 1) for p in signature_header.split(","))
    timestamp = int(parts["t"])
    if abs(time.time() - timestamp) > tolerance_seconds:
        return False
    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.".encode() + payload,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

The 5-minute timestamp tolerance prevents replay attacks. Reject any payload whose

t=
value is older than 5 minutes from your server's current time.

Polling pattern

If you don't use webhooks, poll

GET /v1/simulations/{id}
until
 results_available=true
, then GET the results. Honor the
Retry-After
header — polling faster than 1 req/2s wastes your rate-limit budget without faster results.

Polling is rate limited per the API key tier (see /docs/api-reference). Long-poll abuse is handled at the per-key level. Customer-tier-aware per-simulation limits are roadmap.

Errors

  • 404 workflow_not_found
    — POST: workflow_id doesn't match a published workflow
  • 404 simulation_not_found
    — GET: id doesn't exist or belongs to a different organization
  • 409 simulation_not_complete
    — GET
    /results
    while still
    queued
    /
    running
  • 422 agent_count_exceeds_plan
    — POST: agent_count over your tier ceiling (current v1 ceiling: 10000)
  • 429 rate_limited
    — too many requests; honor the
    Retry-After
    header