Demo jobs
Demo jobs are the normalized Fictix control-plane API for approved server-to-server integrations. Console customers should create/select a company and manage project keys in the Fictix Console first; use the runtime API for the company they already own.
The signed-in Console itself uses a WorkOS-backed bearer session for its management actions. The Console manages that session; it is not a customer credential to copy or forward. This reference documents the approved external server-to-server credential path, which uses the Fictix-supplied admin key.
Base path:
/api/v1/demo-jobs
For the stable response fields, contract version, and error envelope, see
Response Envelopes and the live
/openapi.json.
Create a demo job
/api/v1/demo-jobsPOST /api/v1/demo-jobs
Headers:
Content-Type: application/json
Idempotency-Key: <stable-client-generated-key>
X-Admin-Key: $FICTIX_ADMIN_API_KEY
/demo-jobs is not a self-service login flow. Never use an email/password,
copy a browser session token, or put an admin key in frontend code. Normal
Console customers should use their project X-API-Key on runtime routes
instead.
Request body:
{
"company": {
"name": "Acme Logistics",
"domain": "acme-logistics.example",
"archetype": "professional_services",
"industry": "Logistics",
"employee_count": 120,
"annual_revenue": 24000000,
"customer_accounts": 1500,
"company_age_months": 48,
"selected_services": ["qbo", "gusto", "hubspot", "asana"],
"dials": {}
},
"generation": {
"employees": 120,
"months_of_history": 24,
"quality_preset": "realistic"
},
"simulation": {
"seed": 42
},
"scenarios": [],
"publish": false
}
Required fields
Minimum viable body:
{
"company": {
"name": "Acme Logistics"
},
"generation": {
"employees": 20,
"months_of_history": 6,
"quality_preset": "clean"
}
}
Response
{
"object": "demo_job",
"contract_version": "2026-07-17",
"regeneration_required": false,
"id": "8f3a2d11",
"status": "ready",
"created_at": "2026-07-17T16:22:00Z",
"updated_at": "2026-07-17T16:22:04Z",
"generated_at": "2026-07-17T16:22:04Z",
"links": {
"self": "/api/v1/demo-jobs/8f3a2d11",
"status": "/api/v1/demo-jobs/8f3a2d11/status",
"quality": "/api/v1/demo-jobs/8f3a2d11/quality",
"bundle": "/api/v1/demo-jobs/8f3a2d11/bundle"
},
"company": {},
"runtime": {
"project_id": "8f3a2d11",
"slug": "acme-logistics",
"publish_id": null
},
"dataset_summary": {
"record_counts": {
"customers": 120,
"invoices": 1440
}
},
"quality": {},
"simulation": null,
"scenario_manifest": null,
"credentials": {
"api_key": "fi_live_...",
"api_key_id": "pak_...",
"api_key_prefix": "fi_live_abc..."
},
"credit": null
}
A successful create returns status: "ready". Its top-level envelope is
strict: the required fields are object, contract_version,
regeneration_required, id, status, company, runtime, links,
dataset_summary, quality, and credentials. Lifecycle timestamps,
simulation, scenario_manifest, and the settled credit receipt are
optional. When regeneration_required is true, regenerate the retained
company before importing it as a current-contract company.
Create errors
| Status | Meaning | Client action |
|---|---|---|
400 |
Invalid idempotency key or create payload | Fix the request; use a stable new key for a new create. |
401 / 403 |
Management authentication is invalid, or the caller cannot create in the resolved tenant | Restore approved server-side access; do not expose it to a browser. |
402 |
A valid credit run or sufficient credits is required | Resolve the server-side credit state before retrying. |
409 |
The idempotency key, credit target, or original issuance conflicts | Reuse a key only with the same request; otherwise create a new request. |
422 |
The body, priced generation shape, or credit validation is invalid | Correct the request or credit state. |
429 |
A generation, rate, plan, or concurrency limit was reached | Back off and respect the applicable limit. |
500 / 503 |
A transient server, capacity, storage, or ownership condition | Retry the same request with the same idempotency key and backoff. |
List demo jobs
/api/v1/demo-jobs?limit=100&offset=0GET /api/v1/demo-jobs?limit=100&offset=0
Query params:
| Name | Type | Default | Notes |
|---|---|---|---|
limit |
integer | 100 |
Minimum 1, maximum follows API pagination cap. |
offset |
integer | 0 |
Use for offset pagination. |
Response:
{
"object": "list",
"contract_version": "2026-07-17",
"count": 1,
"jobs": []
}
Get demo job
/api/v1/demo-jobs/{job_ref}GET /api/v1/demo-jobs/{job_ref}
job_ref can be a job id or another supported project reference where
available.
Returns normalized metadata for one job.
Get status
/api/v1/demo-jobs/{job_ref}/statusGET /api/v1/demo-jobs/{job_ref}/status
Returns:
- status
- generated timestamp
- error message
- links
- company summary
- dataset summary
- quality summary
- simulation summary
Use this endpoint for polling and UI state.
Get quality
/api/v1/demo-jobs/{job_ref}/qualityGET /api/v1/demo-jobs/{job_ref}/quality
Returns:
- quality summary
- validation checks
- validation certificate
Use this endpoint before loading or presenting generated data as ready.
Get validation certificate
/api/v1/demo-jobs/{job_ref}/certificateGET /api/v1/demo-jobs/{job_ref}/certificate
Returns the validation certificate for one job. Use it for auditability and data pack handoff.
Get runtime readiness
/api/v1/demo-jobs/{job_ref}/runtime-readinessGET /api/v1/demo-jobs/{job_ref}/runtime-readiness
Returns explicit readiness across static, trickle, and continuous runtime modes.
Get eval pack manifest
/api/v1/demo-jobs/{job_ref}/eval-pack-manifestGET /api/v1/demo-jobs/{job_ref}/eval-pack-manifest
Returns a shareable eval/data-pack contract including certificate, readiness, and scenario information.
Publish
/api/v1/demo-jobs/{job_ref}/publishPOST /api/v1/demo-jobs/{job_ref}/publish
Returns the updated job plus handoff metadata:
{
"handoff": {
"publish_id": "pub_abc123",
"dashboard_url": "https://...",
"staging_urls": {}
}
}
Clone
/api/v1/demo-jobs/{job_ref}/clonePOST /api/v1/demo-jobs/{job_ref}/clone
Creates a new job with a fresh id, slug, seed, publish id, and runtime API key.
Reset
/api/v1/demo-jobs/{job_ref}/resetPOST /api/v1/demo-jobs/{job_ref}/reset
Body:
{
"confirm": true
}
Returns:
{
"object": "demo_job_reset",
"contract_version": "2026-07-17",
"status": "reset",
"job": {}
}
Use reset carefully. Clone is safer for normal iteration because reset destroys current generated state.
