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

POST/api/v1/demo-jobs
POST /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

GET/api/v1/demo-jobs?limit=100&offset=0
GET /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

GET/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

GET/api/v1/demo-jobs/{job_ref}/status
GET /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

GET/api/v1/demo-jobs/{job_ref}/quality
GET /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

GET/api/v1/demo-jobs/{job_ref}/certificate
GET /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

GET/api/v1/demo-jobs/{job_ref}/runtime-readiness
GET /api/v1/demo-jobs/{job_ref}/runtime-readiness

Returns explicit readiness across static, trickle, and continuous runtime modes.

Get eval pack manifest

GET/api/v1/demo-jobs/{job_ref}/eval-pack-manifest
GET /api/v1/demo-jobs/{job_ref}/eval-pack-manifest

Returns a shareable eval/data-pack contract including certificate, readiness, and scenario information.

Publish

POST/api/v1/demo-jobs/{job_ref}/publish
POST /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

POST/api/v1/demo-jobs/{job_ref}/clone
POST /api/v1/demo-jobs/{job_ref}/clone

Creates a new job with a fresh id, slug, seed, publish id, and runtime API key.

Reset

POST/api/v1/demo-jobs/{job_ref}/reset
POST /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.