# Fictix Docs — full content # Generated from the Scalar Docs source. Curated index: /llms.txt # ============================================================ # https://fictix-docs.vercel.app/ # ============================================================ Developer platform / Synthetic company infrastructure # Test your product against a company you can know. Fictix generates a believable, reconciled company across accounting, banking, CRM, payroll, and tasks. Plant known issues, load the data into your product, and measure what it catches—without using real customer data. [Generate a company ↗](https://console.fictix.ai/auth/sign-up?next=%2Forgs) [Read the quickstart →](/get-started/quickstart) First runtime request ## Read a generated company's customers Create or select a company in the Console, then copy its project slug, runtime origin, and project API key from **Connect to your builder**. [Start in the Console →](/get-started/console-onboarding) [Connect a provider API or SDK →](/guides/connect-your-software) ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/customers?limit=10" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` - **Synthetic by construction:** No customer data is ingested. - **Reconciled across systems:** The ledger, bank, CRM, and payroll agree. - **Ground truth included:** You know exactly what your product should find. Build paths ## Go from company to test result Choose the path that matches the job in front of you. - [Connect a company](/get-started/overview) — Use the Console, keep the project key server-side, and make your first runtime read. - [Load believable data](/guides/load-into-your-app) — Page normalized entities, export ledgers, or consume native-shaped connector records. - [Evaluate your product](/guides/evaluate-your-app) — Plant scenarios, preserve the manifest, and score your product against known outcomes. Executable contracts ## Guides and API reference, in one place The narrative docs explain the integration decisions. The Scalar references render the exact request, response, and schema contracts from curated OpenAPI documents. - [Company API](/api/company) — Project-key runtime routes and credential-free capability discovery for Console-generated companies. - [Partner API](/api/partner) — Server-to-server demo jobs, simulation, test matrices, exports, and lifecycle controls. > **Console first:** A normal customer does not create companies through the Partner API. Fictix never asks you to extract a bearer token from a Console session. Keep exploring ## The rest of the system - [Entities and lineage](/concepts/entities-and-lineage) — See how records stay coherent across accounting, banking, CRM, and payroll. - [ASC clinical data](/concepts/clinical-asc) — Generate a synthetic surgery-center dataset with known safety-event ground truth. - [Build with an agent](/for-agents/consuming-these-docs) — Use llms.txt, raw Markdown routes, AGENTS.md, or the Fictix skill pack. - [Ship the integration](/resources/production-checklist) — Review credentials, pagination, retries, compatibility, observability, and rollout checks. # ============================================================ # https://fictix-docs.vercel.app/get-started/overview # ============================================================ # Overview Fictix is an API for generating believable synthetic companies and using them to test software products. A Fictix company can include accounting data, banking activity, customer and vendor records, payroll, CRM data, tasks, native-shaped connector records, a simulation clock, webhooks, and planted scenarios for evaluating whether another application catches known business issues. The most important developer idea is simple: ```text You create a demo job. Fictix creates a generated company. You fetch normalized data, exports, events, or native-shaped records. You load or test that data in your own product. ``` ## Start in the Console The Console is the front door for customers. [Create an account](https://console.fictix.ai/auth/sign-up?next=%2Forgs) or [sign in to projects](https://console.fictix.ai/auth/sign-in?next=%2Fprojects), then [generate a company](https://console.fictix.ai/generate) or select an existing one. Use its **Connect to your builder** panel or [Company settings](https://console.fictix.ai/settings/company) to obtain the runtime URL and project key for server-side code. This keeps company creation, organization access, billing, key rotation, and webhook administration in one customer-facing place. > **Note:** See [Start In The Fictix Console](/get-started/console-onboarding) before following an API quickstart. ## What you can build Use Fictix when you need to: - create a realistic demo tenant without exposing real customer data - populate a B2B SaaS product with believable business activity - generate repeatable accounting, banking, CRM, payroll, task, and commerce data - test ingestion pipelines against coherent cross-system records - run scenario-based QA with planted truth, such as ghost employees, delayed payments, duplicate vendors, or reconciliation issues - give AI agents or eval harnesses a synthetic company to inspect - reproduce a customer-like state for demos, onboarding, QA, or sales engineering ## The main API surface Approved server-to-server integrations use this normalized control-plane surface: **POST** `/api/v1/demo-jobs` ```text POST /api/v1/demo-jobs GET /api/v1/demo-jobs/{job_id} GET /api/v1/demo-jobs/{job_id}/status GET /api/v1/demo-jobs/{job_id}/bundle GET /api/v1/demo-jobs/{job_id}/entities/{entity} GET /api/v1/demo-jobs/{job_id}/entities/{entity}/export ``` The `demo_job` resource wraps the lower-level project/runtime implementation and returns stable links, company metadata, dataset counts, quality status, and runtime identifiers in one place. ## Base URLs Use the base URL for the environment you are targeting: ```text Production: https://api.fictix.ai Staging: https://api-staging.fictix.ai Runtime handoff: https://staging.fictix.dev/{publish_id}/{service}/... ``` If your deployment uses a different host, keep the path structure the same and replace only the origin. ## Authentication models Fictix has a public discovery surface, a Console-managed bearer session, and one external credentialed auth layer: | Layer | Used for | Header | | --- | --- | --- | | Project runtime key | Connect an existing company to server-side code | `X-API-Key: ` | | Console bearer session | Signed-in Console management flows | WorkOS bearer session managed by the Console; never copy it | | External management key | Fictix-approved server-to-server control-plane integrations | `X-Admin-Key: $FICTIX_ADMIN_API_KEY` | > **Warning:** Most customer integrations should use the project runtime key issued in the > Console. The signed-in Console can create or control demo jobs with its managed > WorkOS bearer session. External integrations may do so only with the > Fictix-supplied admin key above. The management surface is not a self-service > browser-key flow, and a Console session must never be copied or forwarded. ## Response objects Fictix responses use object names so clients can branch safely: ```json { "object": "demo_job", "contract_version": "2026-07-17", "id": "8f3a2d11", "status": "ready" } ``` Common object names: - `demo_job` - `demo_job_status` - `demo_job_quality` - `demo_bundle` - `demo_entity_page` - `demo_entity_export` - `catalog_collection` - `test_matrix` - `test_run` ## Environment expectations For a production-grade integration, plan for: - idempotent demo job creation - retries around network failures and `5xx` responses - handling `413` bundle-size responses by paging entities - storing `job_id`, `runtime.project_id`, and `runtime.slug` - never storing plaintext runtime API keys unless you need lower-level runtime access - validating `quality.summary` before presenting generated data as clean - using reserved synthetic domains in generated counterparty data ## First integration decision Before writing code, choose one of these integration modes: | Mode | Use when | Main endpoints | | --- | --- | --- | | Console runtime | You need an existing Console-created company in your app | runtime data, exports, native routes | | Static data | You have approved control-plane access and need a generated company once | create job, status, bundle, entity exports | | Data loader | You need to map Fictix into your own schema | catalog, entities, exports | | Runtime demo | You need ongoing simulated activity | simulation start/status/advance/events | | Eval harness | You need planted truth and scoring | scenarios, manifest, test matrices, test runs | | Native parity | You need API-shaped connector behavior | runtime/native routes after creating a job | # ============================================================ # https://fictix-docs.vercel.app/get-started/console-onboarding # ============================================================ # Start in the Fictix Console Fictix is Console-first. The Console is where a team creates a synthetic company, controls access, manages billing, and obtains the project-scoped key used to connect that company to server-side code. Open the frontend before making your first runtime API request: [create an account](https://console.fictix.ai/auth/sign-up?next=%2Forgs), or [sign in and open your projects](https://console.fictix.ai/auth/sign-in?next=%2Fprojects). ## 1. Sign in and select an organization Sign in, then create or select the organization that owns the test company. New users land in [Organizations](https://console.fictix.ai/orgs) after account creation; existing users can manage organizations at the same frontend route. Organization roles control who can create companies, manage keys, configure webhooks, and manage billing. > **Warning:** Do not share a single organization across unrelated customers or environments. > Use a separate organization or project for each isolated test-company context. ## 2. Create or select a test company Use [Generate a company](https://console.fictix.ai/generate) for a new company, or [open your projects](https://console.fictix.ai/projects) for an existing one. The Console owns the user-facing setup flow; API consumers do not need to construct an internal project record by hand. When generation completes, the project has: - a project id and slug - generated, synthetic company data - selected service capabilities - a project-scoped runtime API key - a Connect panel with copyable server-side setup details ## 3. Create a company API key In staging, the company's **API** page also lets you select an integration, issue a company key, and copy its first request. Follow [Connect your software](/guides/connect-your-software) for the expanded provider catalog and the correct JSON, XML, form, or execute transport. For one key covering all companies in an organization, staging admins and owners can instead use **Organization settings → Organization API keys**. See [One key for multiple companies](/get-started/organization-api-keys). Open [Company settings](https://console.fictix.ai/settings/company), then use **Company API keys** to create a key. Copy the secret when it is shown and store it in your server-side secret manager. The Console only displays the full key at creation or rotation time. Use the key with the generated company's runtime routes: ```http X-API-Key: ``` > **Warning:** Never put this key in browser JavaScript, a public environment variable, a > mobile app, a screenshot, or a git repository. ## Console management session The signed-in Console uses a WorkOS-backed bearer session for its own management actions, including company creation and webhook administration. The Console manages that session in the app; it is not a copyable customer credential, has no environment variable for customers, and must not be forwarded from browser code. ## 4. Use the Connect panel The project's **Connect to your builder** panel gives you the runtime API origin, company slug, company API key, and a sample request. Use the values from that panel to configure your server-side integration: ~~~bash FICTIX_API_ORIGIN="https://api.fictix.ai" FICTIX_PROJECT_SLUG="" FICTIX_PROJECT_API_KEY="" ~~~ FICTIX_API_ORIGIN is the origin only; append the documented route path in your request. For staging, use https://api-staging.fictix.ai and a matching staging company and key. Keep these values in a server-side secret manager or an ignored local environment file. Do not put the key in browser code or a public environment variable. ## 5. Rotate or revoke keys in the Console Project administrators can rotate or revoke keys from the same **Company API keys** area. Rotate a key when it may have been exposed; revoke it when the integration no longer needs access. Update your server-side secret before removing the old key from a live integration. ## Console destinations Every customer-facing setup task starts in the frontend. Link to these routes from a docs-site header, onboarding email, or product handoff instead of asking users to call a management API directly. | Need | Frontend destination | | --- | --- | | Create an account | [Create account](https://console.fictix.ai/auth/sign-up?next=%2Forgs) | | Sign in and see projects | [Sign in](https://console.fictix.ai/auth/sign-in?next=%2Fprojects) | | Create a test company | [Generate](https://console.fictix.ai/generate) | | Open or select a company | [Projects](https://console.fictix.ai/projects) | | Create, rotate, or revoke a company key | [Company settings](https://console.fictix.ai/settings/company) | | Review organization access | [Organizations](https://console.fictix.ai/orgs) | | Manage billing or usage | [Billing](https://console.fictix.ai/billing) | | Explore exports and API setup | [Exports & API](https://console.fictix.ai/app/api) | ## Which API should I use? | Goal | Start here | Auth | | --- | --- | --- | | Connect an existing test company to your app | Runtime routes under `/api/v1/orgs/{slug}` | Project API key from the Console | | Discover available entities and services | `/api/v1/catalog/*` | No credential required | | Create or control companies in the Console | `/api/v1/demo-jobs` | Console-managed WorkOS bearer session (not copyable) | | Create or control companies from an external integration | `/api/v1/demo-jobs` | `FICTIX_ADMIN_API_KEY` / `X-Admin-Key` (Fictix-approved integrations only) | > **Warning:** The `/demo-jobs` control plane is intentionally not a browser-key flow. The > Console UI uses its managed WorkOS bearer session. For an approved > backend-to-backend integration, use `FICTIX_ADMIN_API_KEY` as `X-Admin-Key`, > supplied by Fictix. The Console's WorkOS bearer session is managed by the > Console and is not an external integration credential. Never use > email/password auth, copy a browser session token, or embed the admin key in a > frontend application. Normal customers should use the project `X-API-Key` > runtime flow above. Next: [Runtime And Native Routes](/api-reference/runtime-native-routes) or the [Quickstart](/get-started/quickstart). # ============================================================ # https://fictix-docs.vercel.app/get-started/quickstart # ============================================================ # Quickstart This quickstart connects an application to a synthetic company that already exists in Fictix. To connect many companies with one key, use the staging [organization-key quickstart](/get-started/organization-api-keys). The steps below remain the company-key path, supported in both production and staging. To call a provider API from your own software, follow [Connect your software](/guides/connect-your-software) and the [provider first requests](/api-reference/provider-first-requests). Public catalog snapshots observed on October 28, 2026 report 60 entries in both environments: 59 marked available/selectable and one FCI execute preview that is not selectable. These catalog reports are not production request tests or a strict parity claim; the published first-request templates were tested in staging only. > **Note:** Start in the frontend — [create an account](https://console.fictix.ai/auth/sign-up?next=%2Forgs) > or [sign in to projects](https://console.fictix.ai/auth/sign-in?next=%2Fprojects) — > not by putting a management credential into a browser or a local shell history. > The signed-in Console manages its own WorkOS bearer session; never copy that > session token or forward it to an integration. ## 1. Create or open a company in the Console Sign in, select an organization, then [generate a company](https://console.fictix.ai/generate) or [open a project](https://console.fictix.ai/projects). When it is ready, open its **Connect to your builder** panel. That panel is the source of truth for the company slug, API origin, and the current project API key. If you need a key, use [Company settings](https://console.fictix.ai/settings/company) → **Company API keys**. Copy the full key only once and save it in your server-side secret manager. ## 2. Set server-side environment variables Use values from the Console's Connect panel. A production configuration looks like this: ```bash export FICTIX_API_ORIGIN="https://api.fictix.ai" export FICTIX_PROJECT_SLUG="replace_with_console_slug" export FICTIX_PROJECT_API_KEY="replace_with_console_key" ``` For staging, use `https://api-staging.fictix.ai` and a staging project/key from the Console. > **Warning:** Never mix a production key with a staging origin or vice versa. ## 3. Confirm the company is reachable **GET** `/api/v1/orgs/{slug}/info` ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/info" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` The response identifies the generated company and includes record-count metadata. A `401` or `403` usually means the key is missing, revoked, or belongs to a different project. Rotate or issue a replacement key in the Console rather than sharing a colleague's key. ## 4. Read company data **GET** `/api/v1/orgs/{slug}/data/customers` Fetch a small page first: ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/customers?limit=10" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` Use a bounded page size and continue until the returned count is satisfied. The same project key can read normalized data, export ledgers, and call supported native-shaped service routes. ## 5. Export a ledger **GET** `/api/v1/orgs/{slug}/export/invoices` ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/export/invoices?format=csv" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -o invoices.csv ``` For a full runtime CSV pack, use the export-pack route documented in [Runtime And Native Routes](/api-reference/runtime-native-routes). For a portable partner loader, use the normalized `/api/v1/demo-jobs` entity exports only when your account has approved control-plane access. ## 6. Discover what Fictix supports Catalog endpoints are public discovery endpoints; they contain no tenant data and do not require a project key: **GET** `/api/v1/catalog/entities` **GET** `/api/v1/catalog/services` **GET** `/api/v1/catalog/connector-capabilities` ```bash curl -sS "$FICTIX_API_ORIGIN/api/v1/catalog/entities" | jq curl -sS "$FICTIX_API_ORIGIN/api/v1/catalog/services" | jq curl -sS "$FICTIX_API_ORIGIN/api/v1/catalog/connector-capabilities" | jq ``` Read the catalog before assuming a particular native service/object combination is available. See [Capability Matrix](/api-reference/capability-matrix). ## Minimal server-side loader This Python example follows the count and offset fields in the entity-page response and collects every page. Install its dependency first: ~~~sh python -m pip install requests ~~~ ~~~python ORIGIN = os.environ["FICTIX_API_ORIGIN"].rstrip("/") SLUG = os.environ["FICTIX_PROJECT_SLUG"] HEADERS = {"X-API-Key": os.environ["FICTIX_PROJECT_API_KEY"]} def fetch_customers(page_size: int = 100) -> list[dict]: url = f"{ORIGIN}/api/v1/orgs/{SLUG}/data/customers" rows = [] seen_ids = set() offset = 0 total = None while total is None or offset < total: response = requests.get( url, headers=HEADERS, params={"limit": page_size, "offset": offset}, timeout=30, ) response.raise_for_status() page = response.json() data = page.get("data") count = page.get("count") if not isinstance(data, list) or type(count) is not int or count < 0: raise ValueError("Expected an entity page with data and a non-negative count") if "offset" in page and page["offset"] != offset: raise ValueError(f"Requested offset {offset}, received {page['offset']}") if len(data) > page_size: raise ValueError(f"Received {len(data)} rows for limit {page_size}") if total is not None and count != total: raise ValueError(f"Entity count changed during pagination: {total} to {count}") total = count for row in data: record_id = row.get("id") if isinstance(row, dict) else None if record_id is None or not str(record_id).strip(): raise ValueError(f"Record at offset {offset} has no id") record_id = str(record_id) if record_id in seen_ids: raise ValueError(f"Duplicate record id across pages: {record_id}") seen_ids.add(record_id) rows.append(row) if not data and offset < total: raise ValueError(f"Pagination stopped at {offset} before count {total}") offset += len(data) if len(rows) != total: raise ValueError(f"Fetched {len(rows)} rows, but count is {total}") return rows customers = fetch_customers() ~~~ For a complete runnable Node.js 24 server-side app with a local dashboard and a read-only verifier, download the [customer integration example ZIP](/downloads/fictix-customer-lab.zip). It includes a placeholder `.env.example` and no credentials. Check the [archive manifest](/downloads/fictix-customer-lab.manifest.json) for the included files and their SHA-256 hashes. Its optional native QBO write is an explicitly gated experiment: Fictix's public docs do not define that vendor request body, and connector fidelity and plan entitlement still apply. > **Warning:** Keep the key in your backend or worker environment. Do not run this request > from browser code with the full key exposed. ## Programmatic company creation **POST** `/api/v1/demo-jobs` > **Warning:** For external callers, `POST /api/v1/demo-jobs` remains the normalized > control-plane contract for approved server-to-server integrations. It requires > the Fictix-supplied `FICTIX_ADMIN_API_KEY` / `X-Admin-Key` plus an > `Idempotency-Key`. The signed-in Console uses its managed WorkOS bearer session > instead; it is not the first step for a Console customer, and its session token > must never be copied. Never use email/password auth. See [Demo Jobs](/api-reference/demo-jobs) when your Fictix account has that access. ## Next steps - [Evaluate Your App With Planted Truth](/guides/evaluate-your-app) - [Start In The Fictix Console](/get-started/console-onboarding) - [Runtime And Native Routes](/api-reference/runtime-native-routes) - [Load Fictix Data Into Your App](/guides/load-into-your-app) - [Errors, Limits, And Idempotency](/api-reference/errors-limits-idempotency) # ============================================================ # https://fictix-docs.vercel.app/get-started/authentication # ============================================================ # Authentication Fictix exposes a Console-first access model. Use the narrowest layer that supports your workflow, and obtain customer-facing credentials from the [Fictix frontend](https://console.fictix.ai/auth/sign-in?next=%2Fprojects)—not from browser source code or a shared admin secret. ## Choose a runtime key | Key | Company access | Where to create it | | --- | --- | --- | | Company key | One generated company | Company settings → Company API keys | | Organization key | Every current and future company in one organization | Staging organization settings → Organization API keys | Both use `X-API-Key`. Organization keys are currently available in staging; see [One key for multiple companies](/get-started/organization-api-keys) for company selection, scope settings, and complete examples. ## Project runtime API key For a company you created or selected in the Console, open [Company settings](https://console.fictix.ai/settings/company) and create a project-scoped key under **Company API keys**. This is the normal key for connecting your server to generated company data and native-shaped routes. ```http X-API-Key: ``` > **Note:** The project's **Connect to your builder** panel supplies the exact base URL, > slug, environment variable names, and first request. Project administrators can > rotate or revoke keys in the Console. The full key is only displayed when it is > created or rotated. Use project keys only under the generated company runtime: ```text GET /api/v1/orgs/{slug}/info GET /api/v1/orgs/{slug}/data/{entity} GET /api/v1/orgs/{slug}/export/{entity} GET /api/v1/orgs/{slug}/native/{service}/{object_type} POST /api/v1/orgs/{slug}/native/{service}/{object_type} ``` Do not send a project key to `/api/v1/demo-jobs` control-plane routes. For provider calls, native reads need `read`, and native writes need `write`. Some native reads use POST with JSON, GraphQL, XML, or form bodies. Execute reads need both `read` and `execute`; execute writes need `write` and `execute`. FCI uses the execute transport. The staging company's **API** page provides provider-specific first requests and indicates the required access. See [Connect your software](/guides/connect-your-software) for full examples. ## Console bearer session The signed-in Fictix Console uses a WorkOS-backed bearer session for its own management flows, including creating or controlling companies through `/api/v1/demo-jobs`. The Console manages this session in the app; it is not a customer integration credential, has no customer environment variable, and must never be copied from browser tools or forwarded to another service. ## Approved external management credential External server-to-server callers using management creation and lifecycle routes under `/api/v1/demo-jobs` must use the Fictix-supplied admin key: `FICTIX_ADMIN_API_KEY` as `X-Admin-Key`. Admin-key example: ```http X-Admin-Key: $FICTIX_ADMIN_API_KEY ``` > **Warning:** This credential is not a self-service customer credential. Do not use an > email/password login, copy a bearer token from browser developer tools, or put > the admin key in frontend code. Normal Console customers should create or > select a company in the Console, which manages its own WorkOS bearer session, > and use the project `X-API-Key` for subsequent runtime access instead. ## Idempotency `POST /api/v1/demo-jobs` requires an idempotency key when you have approved control-plane access: **POST** `/api/v1/demo-jobs` ```http Idempotency-Key: ``` Rules: - Use a unique key for each new company creation. - Reuse the same key only when retrying the same request body. - If the same key is reused with a different body, Fictix returns `409`. - Treat `409` as a client bug or stale retry state. Good keys: ```text customer-demo-acme-2026-07-02 load-test-run-7dbe3a8c crm-import-smoke-20260702T120000Z ``` Bad keys: ```text demo test retry ``` ## Tenant scope Approved management integrations are scoped by the Fictix account and tenant permissions attached to their server-side credential. Project runtime keys are scoped to one generated company. For customer-facing products, keep each customer/environment in the appropriate organization and rotate a project key instead of sharing it between unrelated integrations. An organization runtime key spans all companies currently owned by its organization and can list them through `GET /api/v1/organizations/{org_id}/companies`. It does not grant management authority. Use a separate key per integration so each can be revoked independently. ## Browser safety > **Warning:** Never expose: > > - `X-Admin-Key` > - raw company or organization runtime API keys > - Stripe/WorkOS/server secrets If browser code needs to initiate a customer action, keep the credentialed operation in the Fictix Console or proxy it through your backend. A safe flow looks like: ```text Browser -> Fictix Console UI or your backend -> Fictix management API ``` not: ```text Browser -> Fictix with project or admin key in JavaScript ``` ## Common auth failures ### Missing management credentials Symptom: ```json { "detail": "Not authenticated" } ``` Fix: - for an external integration, verify it is approved and send `X-Admin-Key: $FICTIX_ADMIN_API_KEY` - in the Console, stay signed in so the Console can manage its bearer session - verify the environment points to the intended Fictix host ### Wrong auth layer Symptom: ```json { "detail": "Project API key is not valid for this route" } ``` Fix: - for an external integration, use the Fictix-supplied `X-Admin-Key: $FICTIX_ADMIN_API_KEY` for `/demo-jobs`; the Console uses its managed WorkOS bearer session - use company runtime keys under `/orgs/{slug}`; staging organization keys also access their own `/organizations/{org_id}/companies` directory ### Expired bearer token Symptom: ```json { "detail": "Token expired" } ``` Fix: - rotate or renew the approved admin key through Fictix, or sign in again to refresh the Console session - retry once with the renewed credential - never copy a bearer token from browser developer tools or a Console session # ============================================================ # https://fictix-docs.vercel.app/get-started/organization-api-keys # ============================================================ # One key for multiple companies An organization API key lets your server access every current and future company in one Fictix organization. The same key can access 100 companies; each request selects one company by its runtime slug. > **Note:** Organization keys are available in [staging](https://staging.fictix.ai). > Use `https://api-staging.fictix.ai` for the examples on this page. Production > continues to support company keys until this feature reaches production. > Account limits still apply to company creation and usage. ## Create the key 1. Sign in to the staging Console and open the organization that owns your companies. 2. Open its **Settings**, then **Organization API keys**. You must be an organization admin or owner. 3. Choose a name, access level, and expiry, then select **Create organization key**. 4. Copy the full key while it is displayed and save it in your server's secret store. The full secret is displayed only when a key is created or rotated. Key lists show a masked prefix, status, scopes, usage, and expiry. | Access setting | Scopes | Use | | --- | --- | --- | | Read only | `read` | Read company data and list companies | | Read and modify | `read`, `write` | Read and modify supported runtime records | | Full runtime | `read`, `write`, `execute` | Also call the execute transport | Runtime operations retain their existing scope requirements. An organization key does not grant company creation, membership, billing, key-management, or other actions that require a signed-in Console session. Ground-truth access requires the separate, explicit `ground_truth` scope; **Full runtime** does not include it. ## List available companies Keep these values on your server: ```bash export FICTIX_API_ORIGIN="https://api-staging.fictix.ai" export FICTIX_ORGANIZATION_ID="replace_with_organization_id" export FICTIX_ORGANIZATION_API_KEY="replace_with_organization_key" curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/organizations/$FICTIX_ORGANIZATION_ID/companies?limit=100&offset=0" \ -H "X-API-Key: $FICTIX_ORGANIZATION_API_KEY" ``` The organization ID appears in the directory request shown below the key controls. A response has this shape; the identifiers below are fictional: ```json { "organization_id": "example_org", "companies": [ {"id": "company_a", "name": "Fictional Estate Software", "slug": "estate-software-demo", "status": "ready", "org_id": "example_org"}, {"id": "company_b", "name": "Fictional Advisory Services", "slug": "advisory-services-demo", "status": "ready", "org_id": "example_org"} ], "count": 2, "total": 2, "limit": 100, "offset": 0 } ``` For this directory, `count` is the number returned on the current page and `total` is the number in the organization. Continue while `offset + count < total`. The maximum `limit` is 1,000. ## Select a company on each request Change the slug and keep the same key: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/estate-software-demo/data/customers?limit=10" \ -H "X-API-Key: $FICTIX_ORGANIZATION_API_KEY" curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/advisory-services-demo/data/customers?limit=10" \ -H "X-API-Key: $FICTIX_ORGANIZATION_API_KEY" ``` `/organizations/{org_id}` identifies your Fictix organization. The older `/orgs/{slug}` runtime path identifies a generated **company**. For execute request bodies, the legacy `org_id` field is the selected company's `id`; it is not the organization ID from the directory URL. Company keys continue to work for their own company. An organization key cannot access a company owned by another organization. Moving a company out removes that organization's key access; moving one in allows its new organization's keys to access it. ## Rotate and revoke **Rotate** creates a replacement and immediately revokes the previous key, while preserving its scopes and expiry. Save the replacement and update your server. If you need an overlap during rollout, create a second key, update your integrations, and then revoke the first key. **Revoke** stops that key across all companies in the organization. Expired and revoked keys return `401`. A valid key selecting a company outside its organization returns `404` (`Project not found`). If you receive `404`, check the company slug and the organization that owns it. Keep the key out of frontend JavaScript, public environment variables, and source control. The [Company API reference](/api/company) documents runtime requests; routes requiring Console bearer authentication remain separate. # ============================================================ # https://fictix-docs.vercel.app/concepts/demo-jobs # ============================================================ # Demo jobs A demo job is the external resource that represents one generated company. Internally, Fictix stores projects, org-scoped runtime routes, generated datasets, normalized entity shadows, native service records, simulation state, events, validation results, and scenario manifests. Externally, a partner should not need to stitch those pieces together. The `demo_job` resource is the stable wrapper around them. ## Lifecycle ```text created -> generating -> ready \-> error ``` Most current demo-job creates perform generation during the request and return a ready resource. Clients should still handle polling because larger or future async jobs may return before generation is complete. ## Creating a job **POST** `/api/v1/demo-jobs` ```http POST /api/v1/demo-jobs Idempotency-Key: ``` The request has four main blocks: ```json { "company": {}, "generation": {}, "simulation": {}, "scenarios": [] } ``` ### `company` Describes the real or hypothetical business you want Fictix to synthesize. Important fields: - `name` - `domain` - `apollo_id` - `archetype` - `industry` - `employee_count` - `annual_revenue` - `customer_accounts` - `company_age_months` - `selected_services` - `dials` ### `generation` Controls generated data scale and quality. Important fields: - `employees` - `months_of_history` - `quality_preset` Quality presets: - `clean` - `realistic` - `messy` - `stress_test` ### `simulation` Initializes the simulation clock and dials. Important fields: - `seed` - `start_date` - `dials` ### `scenarios` Optional planted-scenario inputs used for QA and eval flows. Example: ```json { "scenarios": [ { "id": "ghost-employee", "params": { "months": 1 } } ] } ``` ## Important response fields ```json { "object": "demo_job", "contract_version": "2026-07-17", "regeneration_required": false, "id": "8f3a2d11", "status": "ready", "links": { "self": "/api/v1/demo-jobs/8f3a2d11", "status": "/api/v1/demo-jobs/8f3a2d11/status", "quality": "/api/v1/demo-jobs/8f3a2d11/quality" }, "company": {}, "runtime": {}, "dataset_summary": {}, "quality": {}, "credentials": {} } ``` ### `id` The partner-facing demo job identifier. Today it maps to the underlying project id. Store this as the primary key in your integration. ### `regeneration_required` When `true`, the retained company predates the current accounting contract. Regenerate it before importing its data into a workflow that requires the current contract. ### `links` Stable follow-up links. Prefer using links over hardcoding paths when building a generic client. ### `runtime` Contains lower-level identifiers: - `project_id` - `slug` - `publish_id` Use `runtime.slug` for runtime/org routes. Use `publish_id` for human handoff URLs. ### `dataset_summary` Contains record counts and entity availability. Use this to decide which loaders to run. ### `quality` Summarized validation status. Use the full quality endpoint for check-level details. ### `credentials` > **Warning:** Includes a runtime API key for optional lower-level runtime access. Store it > only when required. ## Listing jobs **GET** `/api/v1/demo-jobs?limit=100&offset=0` ```http GET /api/v1/demo-jobs?limit=100&offset=0 ``` The list endpoint returns visible jobs for the authenticated user/operator. ## Status polling **GET** `/api/v1/demo-jobs/{job_id}/status` ```http GET /api/v1/demo-jobs/{job_id}/status ``` Use status polling when: - generation may be slow - a UI needs a progress/status state - a worker wants to wait before loading entities ## Resetting jobs **POST** `/api/v1/demo-jobs/{job_id}/reset` ```http POST /api/v1/demo-jobs/{job_id}/reset ``` Reset requires an explicit confirmation body: ```json { "confirm": true } ``` > **Warning:** Reset clears generated runtime state and returns the job to a clean state. It is intended for iterative demo/test flows, not for ordinary data refresh. Prefer clone when you need a new independent company. ## Cloning jobs **POST** `/api/v1/demo-jobs/{job_id}/clone` ```http POST /api/v1/demo-jobs/{job_id}/clone ``` Clone creates a new project with a new runtime key and seed. Use clone when you want a similar company shape but isolated state. ## Publishing jobs **POST** `/api/v1/demo-jobs/{job_id}/publish` ```http POST /api/v1/demo-jobs/{job_id}/publish ``` Publish returns stable handoff metadata: - `publish_id` - dashboard URL - staging URLs Use this when a PM, solutions engineer, or customer engineer needs a stable reference to a generated company. # ============================================================ # https://fictix-docs.vercel.app/concepts/company-blueprints # ============================================================ # Company blueprints Fictix does not treat a generated company as a random pile of rows. It builds a company blueprint that drives the shape of the synthetic business. Console customers choose and review this shape in the [Fictix Console](https://console.fictix.ai). > **Warning:** The request examples below are for approved server-to-server control-plane > integrations, not a reason to expose a management credential in a frontend. The blueprint is how Fictix decides: - what kind of business this is - how many customers should exist - how revenue relates to headcount - which systems should have data - how much data can be generated safely - whether the classification needs human review ## Sources of company shape Fictix can build a blueprint from: - explicit request fields - domain enrichment - Apollo identifiers - industry and company-size hints - archetype overrides - safe defaults when little information is provided Explicit fields win over inferred fields. ## Important request fields ```json { "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"] } } ``` ## `company_dna` and `generation_company_dna` Fictix separates actual business facts from safe generation scale. ### `company_dna` Represents the real or requested company. Examples: - actual headcount - actual annual revenue - actual customer accounts - business family - operating profile ### `generation_company_dna` Represents the scaled shape Fictix will generate. This may be smaller than the real company. For example, a company with 1,000,000 real customer accounts might generate a representative weighted sample instead of one million customer records. Use `generation_company_dna` for loader sizing and expected volume. Use `company_dna` for UI copy and business context. ## Classification review Generated company responses can include: ```json { "company": { "classification": { "business_family": "commerce", "review_required": false } } } ``` > **Note:** If `review_required` is true, a product UI should ask the user to confirm or > override the classification before presenting the dataset as high-confidence. ## Selected services `selected_services` tells Fictix which systems matter for the generated company. Examples: ```json ["qbo", "stripe", "gusto", "hubspot", "asana"] ``` Selected services affect: - visible connector/system record counts - native-shaped runtime records - export pack relevance - scenario and eval usefulness They do not mean Fictix connects to those real systems. Fictix creates synthetic or native-shaped data that resembles those systems. ## Healthcare / ASC profile Fictix's first healthcare vertical is the **ambulatory surgery center (ASC)** profile. Choose it in the Console, or set `company.industry` to `asc_surgical` in an approved server-to-server create request. The profile adds operating rooms, surgical cases, clinical incidents, and dictated safety reports to the otherwise standard synthetic company. Use the industry field for this profile—not `company.archetype`. The generated records are synthetic and contain no real patient data. See [Healthcare / ASC Data](/concepts/clinical-asc) for entity schemas, safety boundaries, and evaluation guidance. ## Safe scaling Fictix enforces generation budgets. When a request is too large, the API returns an actionable `422` with: - estimated weighted load - estimated records - current budget - quality preset - connector count - suggested safe employees or months of history To reduce load: - lower `generation.employees` - lower `generation.months_of_history` - reduce `selected_services` - choose `clean` or `realistic` instead of `stress_test` - lower customer density in the company shape ## Naming Project names are unique within an organization. If the same name is requested twice, Fictix may append a suffix such as: ```text Local SaaS TestCo (2) ``` Store the returned job/company name rather than assuming it exactly matches the request. # ============================================================ # https://fictix-docs.vercel.app/concepts/clinical-asc # ============================================================ # Healthcare data for ambulatory surgery centers Fictix includes an opt-in clinical content pack for **ambulatory surgery centers (ASCs)**. It creates a coherent synthetic facility, procedure schedule, safety-event history, and dictated incident reports for testing healthcare workflows, extraction pipelines, and evaluation harnesses. > **Warning:** The ASC pack is synthetic test data, not a clinical system. It contains no real > patients, patient names, dates of birth, medical-record numbers, or data from > healthcare systems. Do not use it for patient care, clinical decision-making, > or as a compliance certification. ## Create an ASC company In the Console, select **Ambulatory Surgery Center** when generating a company. For an approved server-to-server demo-job integration, use the canonical industry value in `company.industry`: ```json { "company": { "name": "Lakeside Surgery Center", "domain": "lakeside-surgery.example", "industry": "asc_surgical", "employee_count": 48, "selected_services": ["qbo", "gusto"] }, "generation": { "employees": 48, "months_of_history": 18, "quality_preset": "realistic" } } ``` `asc_surgical` is an industry profile, not a standard financial archetype. Set `company.industry` rather than relying on `company.archetype` for this vertical. The pack is additive: non-ASC companies retain the ordinary business dataset and expose no clinical records. ## Clinical entity families ASC companies add four canonical entity families. They use the same paged entity and export routes as the rest of the dataset. | Entity | What it contains | Important fields | | --- | --- | --- | | `operating_rooms` | Operating and procedure rooms in the facility. | `name`, `room_type`, `specialty_tags`, `commissioned_date`, `status` | | `surgical_cases` | Deterministic weekday procedure schedule. | `case_number`, `or_id`, `scheduled_date`, `procedure_code`, `surgeon_name`, `anesthesia_type`, `patient_ref`, `status` | | `clinical_incidents` | Safety events and near misses. | `occurred_at`, `reported_at`, `category`, `harm_level`, `contributing_factors`, `status` | | `dictation_scripts` | Spoken-style incident reports with an extraction answer key. | `incident_id`, `script`, `labels`, `difficulty`, `needle_ids` | `patient_ref` is an opaque synthetic reference such as `PT-00042`; it is never a real patient identifier. Clinical records have deterministic references: rooms link to cases, cases can link to incidents, and dictation scripts link to their incident. Read a runtime page with the project API key: ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/clinical_incidents?limit=100&offset=0" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` Approved partners can use the normalized equivalent: ```text GET /api/v1/demo-jobs/{job_ref}/entities/clinical_incidents GET /api/v1/demo-jobs/{job_ref}/entities/dictation_scripts/export?format=json ``` Check `dataset_summary.record_counts` or the public entity catalog before loading. A zero count means the selected company is not an ASC company, or no records of that family were generated. ## Ground truth for extraction and evaluation Each `dictation_scripts` record is rendered from its structured incident, so its `labels` object is the extraction answer key by construction. This makes it useful for testing speech-to-structure, incident triage, and safety/compliance workflows without having to infer a hidden answer from prose. ASC scenario templates can add harder known conditions to the scenario manifest: - `ambiguous-time-reference` - `duplicate-staff-name` - `unstated-harm-level` - `mid-sentence-correction` - `late-report-gap` For those scenarios, `evidence.expected_extraction` is the graded answer-key convention. Preserve the scenario manifest with your test result, and use the answer key only in your evaluation pipeline—not in a user-facing workflow. ## Load order and exports Load the usual financial entities first. For clinical records, use this order: 1. `operating_rooms` 2. `surgical_cases` 3. `clinical_incidents` 4. `dictation_scripts` Use JSON when your integration needs the nested `labels`, factors, or staff references. CSV is useful for flat export workflows, but it does not replace the full structured evaluation payload. Next: [Entities And Exports](/api-reference/entities-and-exports), [Entities And Lineage](/concepts/entities-and-lineage), and [Simulation, Scenarios & Events](/api-reference/simulation-scenarios-events). # ============================================================ # https://fictix-docs.vercel.app/concepts/entities-and-lineage # ============================================================ # Entities, counts, and lineage Fictix generates canonical business entities and can also expose native-shaped records for selected services. Canonical entities are the stable integration layer. Native-shaped records are for testing product integrations that expect a provider-shaped API. > **Note:** For a Console-created company, obtain the project key and runtime base URL from > the [Fictix Console](https://console.fictix.ai) before reading entities. > Partner `/demo-jobs` page examples are documented separately in [Entities and > Exports](/api-reference/entities-and-exports). Runtime browsing uses > `/api/v1/orgs/{slug}/data/{entity}`. ## Canonical entity families Common canonical entities: - `accounts` - `bank_accounts` - `bank_transactions` - `bills` - `customers` - `customer_payments` - `credit_memos` - `deals` - `employees` - `invoices` - `items` - `journal_entries` - `payroll_runs` - `tasks` - `vendor_payments` - `vendors` Alias behavior: - `payments` resolves to `customer_payments` - `bill_payments` resolves to `vendor_payments` Prefer canonical names in new integrations. ### ASC clinical extensions Companies created with `company.industry: "asc_surgical"` also expose: - `operating_rooms` - `surgical_cases` - `clinical_incidents` - `dictation_scripts` These are additive entity families: they are absent from non-ASC companies. `dictation_scripts.labels` is a synthetic ground-truth extraction answer key, not real clinical documentation. See [Healthcare / ASC Data](/concepts/clinical-asc). ## Counts Fictix exposes counts in multiple places: - `demo_job.dataset_summary.record_counts` - `GET /api/v1/demo-jobs/{job_id}/status` - entity page response `count` - catalog entity metadata Use counts to: - decide which loaders to run - show UI totals - verify exports - detect partial loads ## Runtime pages Project-key runtime entity pages use this response shape: ```json { "data": [], "count": 0, "total_count": 0, "limit": 100, "offset": 0, "next_cursor": null, "service": null } ``` This example shows an empty page. Runtime responses follow the `RuntimeEntityPage` schema in the public [`/openapi.json`](https://api.fictix.ai/openapi.json). `count` is the total for the requested canonical entity; `data` contains the current page. Continue while **offset + data.length < count**. Advance the next offset by the number of rows actually returned, not by the requested page limit. Stop when **offset + data.length >= count**; treat an empty page before the reported count as a partial load that needs investigation. ## Exports Entity exports support: ```text format=json format=csv ``` Use JSON when preserving nested structures matters. Use CSV when the host product has spreadsheet or batch-import workflows. ## Lineage Responses that derive from generated data include lineage metadata where available. Lineage is how an integration can answer: - which generated company produced this record? - which seed/config generated it? - which validation certificate applies? - does this data come from a scenario-injected run? When loading Fictix data into another product, store lineage alongside imported records when possible. Recommended host-product columns: - `fictix_job_id` - `fictix_entity` - `fictix_record_id` - `fictix_seed` - `fictix_dataset_hash` - `fictix_loaded_at` ## Referential integrity Fictix aims to keep core references coherent: - invoices reference customers - bills reference vendors - customer payments reference invoices - applied credit memos link to invoices and match the invoice customer and currency - vendor payments reference bills - bank transactions reference bank accounts - invoice and bill lines reference items - payroll runs reference employees - journal entries balance debit and credit totals - surgical cases reference operating rooms when the ASC pack is selected - clinical incidents can reference surgical cases and their dictation scripts Treat missing references as a data quality defect and inspect `GET /api/v1/demo-jobs/{job_id}/quality`. Runtime-only integrations should record the project slug and the Console generation timestamp with their own loader result, then use the Console or an approved control-plane flow when they need the full certificate. ## Synthetic safety Generated emails and domains should use reserved namespaces such as: - `.example` - `.test` - `.invalid` > **Warning:** This prevents synthetic data from appearing to belong to real companies or real > people. # ============================================================ # https://fictix-docs.vercel.app/concepts/simulation # ============================================================ # Simulation and time Simulation lets a Fictix company continue to change after initial generation. Use it when a static dataset is not enough. Examples: - advance a company clock - generate new activity over time - create webhook events - plant scenarios - inspect an event stream - score whether another product found known issues ## Simulation state Every simulation has: - a company/project id - a clock - a seed - dial configuration - autonomous status - checkpoint history Read status: **GET** `/api/v1/demo-jobs/{job_id}/simulation/status` ```http GET /api/v1/demo-jobs/{job_id}/simulation/status ``` ## Starting simulation **POST** `/api/v1/demo-jobs/{job_id}/simulation/start` ```http POST /api/v1/demo-jobs/{job_id}/simulation/start ``` Example body: ```json { "seed": 42, "start_date": "2026-01-01", "dials": { "chaos": 0.1, "fraud": 0.0 } } ``` > **Note:** If simulation state already exists, the route returns the existing normalized > state rather than creating a duplicate simulation. ## Advancing time **POST** `/api/v1/demo-jobs/{job_id}/simulation/advance` ```http POST /api/v1/demo-jobs/{job_id}/simulation/advance ``` Example: ```json { "days": 30, "hours": 0, "minutes": 0 } ``` The delta must be positive and within the configured advance limit. ## Single step **POST** `/api/v1/demo-jobs/{job_id}/simulation/step` ```http POST /api/v1/demo-jobs/{job_id}/simulation/step ``` Use step when you want one deterministic increment rather than a large time advance. ## Pause and resume **POST** `/api/v1/demo-jobs/{job_id}/simulation/pause` **POST** `/api/v1/demo-jobs/{job_id}/simulation/resume` ```http POST /api/v1/demo-jobs/{job_id}/simulation/pause POST /api/v1/demo-jobs/{job_id}/simulation/resume ``` Pause stops autonomous progression. Resume restarts it. ## Dials **PATCH** `/api/v1/demo-jobs/{job_id}/simulation/dials` ```http PATCH /api/v1/demo-jobs/{job_id}/simulation/dials ``` Example: ```json { "trickle_rate": "10x", "fraud": 0.2, "chaos": 0.1 } ``` > **Warning:** Dials control behavior intensity. Treat unknown dials as unstable unless they > are documented in the catalog or scenario docs. ## Checkpoints Create checkpoint: **POST** `/api/v1/demo-jobs/{job_id}/simulation/checkpoint` ```http POST /api/v1/demo-jobs/{job_id}/simulation/checkpoint ``` List checkpoints: **GET** `/api/v1/demo-jobs/{job_id}/simulation/checkpoints` ```http GET /api/v1/demo-jobs/{job_id}/simulation/checkpoints ``` Restore checkpoint: **POST** `/api/v1/demo-jobs/{job_id}/simulation/restore/{checkpoint_id}` ```http POST /api/v1/demo-jobs/{job_id}/simulation/restore/{checkpoint_id} ``` Use checkpoints for eval loops, demos, and repeatable QA. ## Events Read event feed: **GET** `/api/v1/demo-jobs/{job_id}/events?limit=50` ```http GET /api/v1/demo-jobs/{job_id}/events?limit=50 ``` Read one event: **GET** `/api/v1/demo-jobs/{job_id}/events/{event_id}` ```http GET /api/v1/demo-jobs/{job_id}/events/{event_id} ``` Events are the bridge between simulation and host-product reactions. A future customer-facing integration can subscribe to webhooks, poll events, or both. ## Scenario manifest Scenario injection can plant truth into the company. The manifest describes what was planted. **GET** `/api/v1/demo-jobs/{job_id}/simulation/manifest` ```http GET /api/v1/demo-jobs/{job_id}/simulation/manifest ``` Use the manifest for: - test harness setup - evaluating detected findings - explaining what broke - reproducing failed app behavior # ============================================================ # https://fictix-docs.vercel.app/guides/connect-your-software # ============================================================ # Connect your software to Fictix Point your server's provider client at a generated company's Fictix URL, add its runtime API key, and send a supported provider request. Records and writes belong to that synthetic company. Fictix does not log in to your vendor account, send real messages, run ads, or move money through a bank. ## 1. Choose the environment and company Public catalog snapshots observed on October 28, 2026 report 60 entries in both staging and production: 59 marked available/selectable and one FCI execute preview that is not selectable. All 60 report approximate fidelity. These are catalog reports, not proof that provider requests work or strict upstream API parity. The published first-request templates were tested in staging only. Use the [first-request directory](/api-reference/provider-first-requests) to check catalog status for each provider; [workflow coverage](/api-reference/provider-workflows) lists the supported staging operations and limits. To evaluate the expanded integrations, sign in at [staging.fictix.ai](https://staging.fictix.ai), create or select a company, and choose the integrations your software needs. Onboarding, the connector picker, and the add-integration menu use the deployed catalog. Your organization's plan and the company's selected services still determine access. Open the company's **API** page to select a provider and copy its API URL, key, and first request. The **Connect to your builder** panel also provides setup details. Keep the key and API origin in the same environment. ## 2. Choose a runtime key Use a **Company API key** for one company. For one key across your current and future companies, staging organization admins can use an [organization API key](/get-started/organization-api-keys). Both authenticate with `X-API-Key`; neither is a vendor OAuth token. | Operation | Required runtime-key scopes | | --- | --- | | Native read, including a supported read sent with POST | `read` | | Native create, update, or delete | `write` | | Execute a read capability, including FCI | `read`, `execute` | | Execute a capability that writes records | `write`, `execute` | Choose the scopes needed by your app when issuing the key. The Console's **Full runtime** organization-key setting includes `read`, `write`, and `execute`. It does not include the separate `ground_truth` scope. Keys never override plan entitlements or grant management access. Keep credentials in your server's secret store. A browser app calls your own server, which attaches the key when calling Fictix. Use the existing [browser-builder proxy guidance](/resources/agent-integration) for browser apps. ## 3. Set the base URL Copy the slug and key from the selected staging company: ```bash export FICTIX_API_ORIGIN="https://api-staging.fictix.ai" export FICTIX_PROJECT_SLUG="replace_with_company_slug" export FICTIX_PROJECT_API_KEY="replace_with_company_key" ``` These examples use a company key. For an organization key, send `$FICTIX_ORGANIZATION_API_KEY` in the same header and select a company by its slug. The organization-key guide shows how to list your company IDs and slugs. | URL | Purpose | | --- | --- | | `/api/v1/orgs/{slug}` | Company runtime base | | `/api/v1/orgs/{slug}/native/{provider}` | Native provider base | | `/api/v1/orgs/{slug}/execute` | Execute transport, including FCI | For example, Stripe's provider base is `https://api-staging.fictix.ai/api/v1/orgs/{slug}/native/stripe`. Append `v1/customers` for its first read: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/stripe/v1/customers" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` If an SDK adds its own `/v1`, `/api`, or service prefix, configure its server URL so the resulting request matches the full path in the [first-request directory](/api-reference/provider-first-requests). Configure its transport to send the Fictix header. An SDK that cannot change its host or headers needs a custom transport or a direct HTTP client. Vendor login flows and SDK defaults are not Fictix authentication. ## 4. Check discovery and the response The catalog is public. Query the environment your server actually uses: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/catalog/connector-capabilities" ``` Find your provider by `id`. Check `status`, `ui_selectable`, `native_supported`, `contract_surfaces`, and `limitations`. FCI has an execute surface and no native surface. A missing provider is unavailable in that environment. Relay Financial has no implemented direct API contract and is not in the available catalog. Provider responses retain their own envelopes, pagination, and content types: - REST requests usually use JSON, but their list fields differ by provider. - Linear and Monday use GraphQL over POST. Inspect the GraphQL `errors` field. - Sage Intacct uses XML. Inspect every returned operation status. - Expensify uses a URL-encoded `requestJobDescription` form field. Check `responseCode` as well as the HTTP status. - Execute responses can use HTTP 200 with `status: "error"`. Require `status: "success"` before consuming `outputs`. Use the request bodies and media types in the first-request directory. A POST read does not become a write merely because of its HTTP method. ## 5. Use FCI through execute Read the company's `id` from `GET /api/v1/orgs/{slug}/info`, the API page's generated request, or the organization company directory. Put that **company project ID** in the legacy `org_id` body field. The workspace organization ID and the URL's company slug are different identifiers. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/execute" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"org_id":"replace_with_company_project_id","user_id":"your-integration","turn_id":"replace_with_unique_request_id","capability_key":"fci.cash","args":{}}' ``` This key needs both `read` and `execute`. FCI returns derived financial summaries and retains preview status. Confirm its capability is available for your company; it is not selectable in the standard connector picker. There is no `/native/fci` first request. ## 6. Make writes reproducible Native writes should include a fresh `Idempotency-Key` per operation. Reuse it only for a retry with the same request and payload. Execute requests use a unique `turn_id` per operation; preserve it on retries. Follow the provider's documented exception where its own idempotency field is supported. Read the resulting object back to verify persistence. Use each provider's pagination contract; canonical `limit`/`offset` is not a universal native API pagination scheme. Unsupported operations return errors rather than extending the vendor contract automatically. If a call returns `401`, check the environment, expiry, and revocation state of the key. A `402` with `feature_not_entitled` means the organization lacks the required plan feature; native integration APIs require `native.genome`, available on Developer or higher. Creating a new key does not change plan access. Canonical REST data can still be available on your existing plan. For `403`, check the key's company or organization and required scopes. For `400`, `404`, or `422`, compare the exact method, path, body, and content type with the documented workflow. The [Company API reference](/api/company) describes runtime routing and schemas. The provider guides supply the concrete vendor paths hidden behind its generic native route. Use the [Partner API](/api/partner) only for an approved external management integration. # ============================================================ # https://fictix-docs.vercel.app/guides/quickbooks-accounting # ============================================================ # Create and pay QuickBooks test documents > **Staging verified:** This workflow passed on November 3, 2026 with a > Console-created synthetic company and ordinary runtime credentials. > Production qualification is pending. Supported operations are described below. Use a synthetic company with QuickBooks selected and a Developer or higher plan with native API access. Generation credits and native API access are separate; adding generation credits does not enable native routes. See [Connect your software](/guides/connect-your-software) for current access requirements. Create the company in the Console, then create a **Read and modify** company key in **Settings → Company → Company API keys**. Keep it on your server. You can also use a staging [organization key](/get-started/organization-api-keys) with the same access for the organization that owns the company. Use the matching origin and slug from that company's Connect panel: ```bash export FICTIX_API_ORIGIN="https://api-staging.fictix.ai" export FICTIX_PROJECT_SLUG="" export FICTIX_PROJECT_API_KEY="" QBO_BASE="$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/qbo/v3/company/test" ``` These requests change only the synthetic company's records. They do not send money through a real QuickBooks or bank connection. ## Read the IDs your application will use Query customers, vendors, items and ledger accounts. Replace `Account` in this request with `Customer`, `Vendor` or `Item` for the other lists: ```bash curl --fail-with-body -sS --get "$QBO_BASE/query" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ --data-urlencode 'query=SELECT * FROM Account STARTPOSITION 1 MAXRESULTS 1000' ``` Read the records inside `QueryResponse.Account`, `QueryResponse.Customer`, `QueryResponse.Vendor` and `QueryResponse.Item`. Use their returned `Id` values in native references. In particular, `AccountRef.value` refers to a native `Account.Id`; do not substitute a different ID from an export or canonical data response. Choose an active service item with an income-account mapping, an expense account, and the bank-linked cash account for this company. Invoice creation resolves the item's income account. Payment posting requires exactly one active bank account linked to the selected cash ledger account, using the document's currency. An ambiguous or missing mapping returns a validation error before posting. The [query reference](/api-reference/provider-workflows#quickbooks-query-and-amount-behavior) describes supported filters and paging. Queries and single-record GETs also support `Invoice`, `Bill`, `Payment`, `BillPayment` and `JournalEntry`. ## Create an invoice Save this as `invoice.json`, replacing the customer and item IDs with values you just read. Use your transaction date in `YYYY-MM-DD` format. ```json { "CustomerRef": {"value": ""}, "TxnDate": "2026-11-03", "Line": [{ "Amount": 0.01, "DetailType": "SalesItemLineDetail", "SalesItemLineDetail": { "ItemRef": {"value": ""}, "Qty": 1, "UnitPrice": 0.01 } }] } ``` ```bash curl --fail-with-body -sS "$QBO_BASE/invoice" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: qbo-invoice-demo-1" \ --data-binary @invoice.json ``` A successful response contains `Invoice.Id`, `TotalAmt: 0.01` and `Balance: 0.01`. Creation posts the receivable and revenue. It does not record a cash receipt. Save `Invoice.Id` for the payment. Use a distinct idempotency key for each operation. Persist the key and exact payload before sending. If the response is lost, retry with that same key and body; changing the payload under the same key returns `409`. Use a new key for a genuinely new invoice. ## Apply the customer's payment Save this as `payment.json`. Use the same customer, the cash account's native ID, and the returned invoice ID: ```json { "CustomerRef": {"value": ""}, "TxnDate": "2026-11-03", "TotalAmt": 0.01, "DepositToAccountRef": {"value": ""}, "Line": [{ "Amount": 0.01, "LinkedTxn": [{"TxnId": "", "TxnType": "Invoice"}] }] } ``` ```bash curl --fail-with-body -sS "$QBO_BASE/payment" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: qbo-payment-demo-1" \ --data-binary @payment.json ``` The response contains `Payment.Id`. Read the invoice again at `GET /invoice/{Invoice.Id}`: its balance should now be zero. The payment creates the linked bank transaction and balanced cash/receivable posting. ## Create a vendor bill Save this as `bill.json` using the vendor and expense account IDs: ```json { "VendorRef": {"value": ""}, "TxnDate": "2026-11-03", "Line": [{ "Amount": 0.01, "DetailType": "AccountBasedExpenseLineDetail", "AccountBasedExpenseLineDetail": { "AccountRef": {"value": ""} } }] } ``` ```bash curl --fail-with-body -sS "$QBO_BASE/bill" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: qbo-bill-demo-1" \ --data-binary @bill.json ``` A successful response contains `Bill.Id` and an outstanding balance of `0.01`. The posting increases expense and accounts payable; cash is unchanged. ## Pay the bill Save this as `billpayment.json`. This workflow supports `PayType: "Check"`: ```json { "VendorRef": {"value": ""}, "TxnDate": "2026-11-03", "TotalAmt": 0.01, "PayType": "Check", "CheckPayment": {"BankAccountRef": {"value": ""}}, "Line": [{ "Amount": 0.01, "LinkedTxn": [{"TxnId": "", "TxnType": "Bill"}] }] } ``` ```bash curl --fail-with-body -sS "$QBO_BASE/billpayment" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: qbo-billpayment-demo-1" \ --data-binary @billpayment.json ``` Read `GET /bill/{Bill.Id}` to confirm a zero balance. The payment creates the linked bank withdrawal and balanced payable/cash posting. Retrying an operation with its original key and payload must not create another payment. ## Check the resulting books Query `JournalEntry`. Each line contains a numeric `Amount`, `DetailType: "JournalEntryLineDetail"`, and a `JournalEntryLineDetail` object with `PostingType: "Debit"` or `"Credit"` and a resolvable `AccountRef`. Each journal's debit and credit amounts must balance. Existing document IDs must remain stable as your application writes new records. The four operations above create four balanced journals. After both exact payments, the new invoice and bill have zero outstanding balance, and their opposite cash movements cancel. You can also read canonical entities or download a [CSV export](/guides/load-into-your-app) to reconcile the company's receivables, payables and bank balances with its ledger. ## Supported slice This guide covers new single-currency, non-tax service invoices, expense bills, and exact payments linked to one existing document. The party, currency, amount, active accounts and mappings must agree. Unsupported invoice or bill corrections return `409`; creating a payment does not authorize editing a posted document. Inventory, tax and currency-conversion workflows are outside this example. Direct `JournalEntry` writes are rejected; journal reads expose the actual posted accounting consequences. Validation errors return a QBO `Fault` or a Fictix error body. Check the HTTP status and body before treating an operation as accepted. The broader [provider workflow reference](/api-reference/provider-workflows) and [Intuit's accounting documentation](https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/invoice) describe the wire conventions; supported Fictix operations remain bounded by this guide. # ============================================================ # https://fictix-docs.vercel.app/guides/build-demo-tenant # ============================================================ # Build a demo tenant This guide shows how to turn a user request into a demo tenant in your product. For a standard Fictix customer, create the company in the [Fictix Console](https://console.fictix.ai) and connect it with a project runtime key. > **Warning:** The programmatic control-plane flow below is for approved backend-to-backend > integrations. Configure the Fictix-supplied `FICTIX_ADMIN_API_KEY` as > `X-Admin-Key` server-side. The signed-in Console uses its own managed WorkOS > bearer session for Console actions; that session is not a customer credential to > copy or forward. Never use an email/password login, copy a browser session > token, or put the admin key in frontend code. ## Goal Given a user selection like: ```text Create a mid-market logistics company with QBO, Gusto, HubSpot, and Asana data. ``` Your system should: 1. create a Fictix demo job 2. wait for readiness 3. fetch entity data 4. map it into your product schema 5. store lineage 6. show the user a ready demo tenant ## Recommended architecture ```text Browser -> your backend -> Fictix approved server-to-server demo-jobs API -> your loader workers -> your database -> browser sees ready tenant ``` Do not call Fictix with management credentials directly from the browser. ## Step 1: Build the create request Map your UI form into the Fictix create payload: ```json { "company": { "name": "Acme Logistics", "domain": "acme-logistics.example", "industry": "Logistics", "employee_count": 120, "annual_revenue": 24000000, "customer_accounts": 1500, "selected_services": ["qbo", "gusto", "hubspot", "asana"] }, "generation": { "employees": 120, "months_of_history": 24, "quality_preset": "realistic" }, "simulation": { "seed": 42 } } ``` Use `.example` domains for clearly synthetic companies. ### ASC healthcare tenant For a synthetic ambulatory surgery center, use `company.industry` rather than `company.archetype`: ```json { "company": { "name": "Lakeside Surgery Center", "domain": "lakeside-surgery.example", "industry": "asc_surgical" } } ``` The resulting company adds synthetic clinical entities. It contains no real patient data; see [Healthcare / ASC Data](/concepts/clinical-asc) before loading or evaluating it. ## Step 2: Create job with idempotency Generate an idempotency key from your internal request id: ```text tenant-create-{tenant_request_id} ``` Then call: **POST** `/api/v1/demo-jobs` ```http POST /api/v1/demo-jobs ``` Store: - Fictix job id - original request - returned company metadata - status - links - runtime ids ## Step 3: Wait for readiness Poll: **GET** `/api/v1/demo-jobs/{job_id}/status` ```http GET /api/v1/demo-jobs/{job_id}/status ``` Treat `status=ready` as generation complete, then inspect quality: **GET** `/api/v1/demo-jobs/{job_id}/quality` ```http GET /api/v1/demo-jobs/{job_id}/quality ``` > **Warning:** Do not show a polished demo tenant if quality is unacceptable for your use case. ## Step 4: Load core records Recommended first load: 1. `accounts` 2. `bank_accounts` 3. `items` 4. `customers` 5. `vendors` 6. `employees` 7. `invoices` 8. `credit_memos` 9. `bills` 10. `customer_payments` 11. `vendor_payments` 12. `bank_transactions` 13. `journal_entries` 14. `deals` 15. `tasks` 16. `payroll_runs` For ASC companies, add `operating_rooms`, `surgical_cases`, `clinical_incidents`, and `dictation_scripts` after the standard entities. Fetch pages: **GET** `/api/v1/demo-jobs/{job_id}/entities/{entity}?limit=500&offset=0` ```http GET /api/v1/demo-jobs/{job_id}/entities/{entity}?limit=500&offset=0 ``` ## Step 5: Store lineage For each imported record, store: - `fictix_job_id` - `fictix_entity` - `fictix_record_id` - `fictix_loaded_at` - `fictix_dataset_hash`, if available Lineage makes support, debugging, and replays possible. ## Step 6: Expose tenant state to users Show: - company name - generated systems - loaded record counts - generation quality - last loaded timestamp - any warnings Avoid showing raw Fictix credentials in UI. ## Failure handling | Failure | User-facing behavior | Backend action | | --- | --- | --- | | Auth failure | Route a normal customer to the Console; approved integrations should contact Fictix support | Verify exactly one approved management credential; do not retry with a project key, email/password, or browser session token | | Generation budget exceeded | Ask user to reduce scale | Lower employees/months/services | | Bundle too large | Continue loading by entity | Use entity pages | | Quality failed | Show warning or block launch | Inspect quality endpoint | | Loader partial failure | Show partial state | Resume from last entity/page | ## Production checklist - idempotency key is stable per user request - no management or project key in browser - generated data is marked synthetic in your product - loader is resumable - lineage is stored - quality endpoint is checked - counts are reconciled after load - runtime API key is encrypted or not stored # ============================================================ # https://fictix-docs.vercel.app/guides/load-into-your-app # ============================================================ # Load Fictix data into your app For a product that already calls a provider API or SDK, use [Connect your software](/guides/connect-your-software) and its provider-specific request directory. This page covers loading canonical data and exports. This guide covers ingestion design for products that want Fictix data inside their own database. Start by creating or selecting the company in the [Fictix Console](https://console.fictix.ai), then copy its server-side runtime setup from **Connect to your builder**. This guide assumes you have a project slug and project `X-API-Key`; it does not require a shared admin key. ## Choose JSON or CSV Use JSON when: - you control the loader - nested lines/applications matter - you want full record fidelity - you need lineage fields Use CSV when: - your product already has CSV importers - a user needs spreadsheet review - a batch pipeline expects files ## JSON loader pattern 1. Get record counts from runtime info or job status. 2. Determine load order. 3. Page each entity. 4. Upsert into your database. 5. Store Fictix lineage. 6. Reconcile counts. Pseudocode: **GET** `/api/v1/orgs/{slug}/data/{entity}` ```python import requests def load_entity(origin: str, slug: str, api_key: str, entity: str): offset = 0 limit = 500 total = None rows = [] seen_ids = set() while total is None or offset < total: response = requests.get( f"{origin}/api/v1/orgs/{slug}/data/{entity}", headers={"X-API-Key": api_key}, params={"limit": limit, "offset": offset}, timeout=30, ) response.raise_for_status() page = response.json() data = page.get("data") current_total = page.get("count") if not isinstance(data, list) or type(current_total) is not int or current_total < 0: raise ValueError("Expected an entity page with data and a non-negative count") if "offset" in page and page["offset"] != offset: raise ValueError(f"Requested offset {offset}, received {page['offset']}") if len(data) > limit: raise ValueError(f"Received {len(data)} rows for limit {limit}") if total is not None and current_total != total: raise ValueError(f"Entity count changed during pagination: {total} to {current_total}") total = current_total if len(rows) + len(data) > total: raise ValueError(f"Fetched more than the reported entity count {total}") for row in data: record_id = row.get("id") if isinstance(row, dict) else None if record_id is None or not str(record_id).strip(): raise ValueError(f"Record at offset {offset} has no id") record_id = str(record_id) if record_id in seen_ids: raise ValueError(f"Duplicate record id across pages: {record_id}") seen_ids.add(record_id) rows.append(row) if not data and offset < total: raise ValueError(f"Pagination stopped at {offset} before count {total}") for row in data: upsert_row(entity, row, lineage=page.get("lineage")) offset += len(data) if len(rows) != total: raise ValueError(f"Fetched {len(rows)} rows, but count is {total}") return len(rows) ``` ## CSV loader pattern Export one entity: **GET** `/api/v1/orgs/{slug}/export/{entity}?format=csv` ```bash curl "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/export/customers?format=csv" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -o customers.csv ``` Then run your import: ```bash your-importer customers.csv --entity customers ``` For accounting workflows, prefer normalized line files when using CSV bundles from runtime export packs. ## Mapping notes ### Customers Map: - `id` to external id - `name` or `display_name` to customer display name - `email` to primary email - address fields to billing address - payment terms to customer settings ### Invoices Map: - invoice header fields first - line items second - customer references after customers are loaded - item references after items are loaded Load **credit_memos** after invoices. Each applied credit memo links to an invoice through **invoice_id**. Confirm each memo's **customer_id** and **currency** match the invoice. Sum **amount_applied** from credit memos by **invoice_id**; keep the invoice's **amount_paid** as cash receipts only. Validate in integer cents: ~~~text applied_credit_memos = sum(amount_applied for credit_memos with invoice_id == invoice.id) invoice.total - invoice.amount_paid - applied_credit_memos - invoice.balance = 0 ~~~ Do not fold applied credits into **invoice.amount_paid**. ### Credit memos The **credit_memos** entity contains applied accounts receivable credits linked to invoices. Read **invoice_id** and **amount_applied** from each memo and group the applied amount by invoice. Load invoices first so the linked invoice is available when importing a memo. ### Bills Load vendors before bills. Validate: ```text total - amount_paid - balance = 0 ``` ### Payments Use canonical names: - `customer_payments`, not `payments` - `vendor_payments`, not `bill_payments` This prevents downstream double-counting. ### Bank transactions Load bank accounts first. Every `bank_account_id` should resolve to a bank account or bank-designated account record. ### Journal entries Validate that each entry balances: ```text sum(debits) = sum(credits) ``` ## Reconciliation after load After loading, compare: - Fictix source count - fetched count - imported count - rejected count - duplicate count Example reconciliation record: ```json { "entity": "invoices", "fictix_count": 200, "fetched_count": 200, "imported_count": 200, "rejected_count": 0 } ``` ## Handling partial loads Make loaders resumable by storing: - project slug or job id - entity - last offset - page size - import checkpoint id - error detail > **Warning:** Do not restart from scratch unless your upsert semantics are idempotent. ## Avoiding synthetic data leakage Mark imported records: - source: `fictix` - synthetic: `true` - demo tenant id - generated timestamp > **Warning:** Do not send Fictix synthetic emails to real email workflows unless your product > has an explicit demo-mode mail sink. ## Approved control-plane integrations **POST** `/api/v1/demo-jobs` If your backend has approved `/api/v1/demo-jobs` access, it can create jobs and use normalized entity pages/exports directly. > **Warning:** Keep that credentialed flow on your server; the Console remains the customer-facing source for organizations, > projects, key rotation, billing, and support context. # ============================================================ # https://fictix-docs.vercel.app/guides/evaluate-your-app # ============================================================ # Evaluate your app with planted truth Create a company in the [Fictix Console](https://console.fictix.ai), plant a supported issue, run your detector against the company records, and submit its findings for a score. The company-key REST flow below uses the company you already created in the Console. ## 1. Prepare the company and keys Follow the [Quickstart](/get-started/quickstart) to create a synthetic test company and get its slug, API origin, and company key. Use the matching production or staging origin throughout. In **Settings → Company → Company API keys**, choose **Read and modify** for the server-side evaluation harness. Planting scenarios and creating test matrices/runs require `write`; reading company records requires `read`. Give the application being evaluated a separate **Read only** key. Keep both keys on their respective servers, using `FICTIX_PROJECT_API_KEY` in each process. Neither process needs a copied Console session or an admin key. The commands below use the harness key and the three environment variables from the quickstart. ## 2. Choose and plant one supported case Read the library before choosing a case: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/scenarios" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` Check the template's `entity_types_affected` and parameter definitions. `duplicate-expense-fraud` plants duplicate **vendor bills**. The current library does not include a duplicate-customer-invoice template. Initialize simulation state, then pause company activity while you evaluate. Starting an existing simulation returns its current state; it does not create another company. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/simulation/start" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"seed":42,"dials":{"trickle_rate":"paused"}}' curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/simulation/pause" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` Plant two duplicate bills with no amount variation: ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/simulation/scenarios" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"scenarios":[{"id":"duplicate-expense-fraud","params":{"count":2,"amount_variance_pct":0}}]}' ``` Inject once. If the connection fails before you receive a response, check the company and scenario state before repeating a write. Scenarios consume the company's scenario quota. ## Runnable duplicate-bill example Download the [customer integration example](/downloads/fictix-customer-lab.zip), configure the quickstart's three server-side variables, and use its exact bill-repeat detector: ```bash npm run evaluate:bills npm run evaluate:bills -- --submit ``` The first command reads bills and prints findings. The second also creates a matrix and submits one test run using your **Read and modify** harness key. It prints the actual score and run ID. It uses only company-key REST routes, without fetching the planted-truth manifest. The example compares exact amounts, matching the zero-variance case above. You can use this command instead of manually performing steps 3–5 below. ## 3. Run your application against the records Use the application's read-only key to page `/data/bills`, or load an export into your own application. Preserve each record's Fictix `id`. Your detector should inspect business fields such as vendor, bill number, currency, amount, and dates. Keep planted-truth manifests outside the detector. Collect the actual record IDs it identifies: ```json { "detected": [ { "entity_type": "bill", "entity_id": "replace_with_an_id_your_detector_found", "finding": "Repeated bill number and amount for the same vendor" } ] } ``` Save this as `detector-findings.json`. Use real IDs from your detector; the placeholder above is not a detection. An empty `detected` array is a valid no-findings result. ## 4. Create a test matrix With the harness key, create a matrix for the case you planted: ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/test-matrices" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"name":"Duplicate bill evaluation","scenarios":[{"scenario_id":"duplicate-expense-fraud","severity":"high","required":true}]}' \ -o matrix.json ``` The response contains `matrix_id`. Build a submission using that returned ID: ```bash jq --arg matrix_id "$(jq -r '.matrix_id' matrix.json)" \ '{matrix_id:$matrix_id,detected:.detected}' detector-findings.json > test-run.json ``` ## 5. Submit findings and read the score ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/test-runs" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @test-run.json \ -o result.json ``` Read `overall.overall_recall`, `overall.overall_precision`, `overall.overall_f1`, and `overall.matrix_passes` for the aggregate result. `results` contains scenario-level recall, precision, missed needles, and false positives. Retain `run_id`, `manifest_hash`, and your application/model version so you can compare later runs against the same evidence. Test runs consume the company's test-run quota. Retrieve a previous run with `GET /api/v1/orgs/{slug}/test-runs/{run_id}`. To list a matrix's history, use `GET /api/v1/orgs/{slug}/test-runs?matrix_id={matrix_id}`; the matrix ID is required. Inspect that history before retrying an uncertain submission. A no-findings submission should record missed planted issues. HTTP 401/403, invalid JSON, or a failed detector request are integration failures rather than successful evaluation results. ## Use Console Test Lab with your detector endpoint You can also create a scenario in **Scenarios**, then open **Test Lab**, enter your detector URL, and choose **Run tests**. The Console handles the matrix and scoring requests with your signed-in session. The Console sends an HTTP POST from your browser to the detector: ```json { "mode": "fictix-test-run", "project_slug": "your-company-slug", "project_id": "your-company-project-id", "scenario_ids": ["duplicate-expense-fraud"] } ``` Return HTTP 200 with a JSON `detected` array in the format shown above. The endpoint must respond within 30 seconds. HTTPS is supported; local development also permits HTTP on `localhost` or `127.0.0.1`. For localhost tests in Chrome, allow the Console site's [local network permission](https://developer.chrome.com/docs/modern-web-guidance/guides/security/local-network-access) when prompted. If you blocked it, enable local network or loopback access in the site's browser permissions and retry. A blocked request can time out before your detector receives it. Allow the calling Console origin through CORS: `https://console.fictix.ai` for production or `https://staging.fictix.ai` for staging. Handle the OPTIONS preflight and allow POST with the `Content-Type` header. The Console sends no detector authorization header and never forwards your Fictix company key. Your detector server supplies its own read-only company key when reading records. ## Approved demo-job integrations Fictix-approved management integrations can use the separate [demo-job API](/api-reference/demo-jobs) and its [test-matrix/run routes](/api-reference/test-matrices-runs). Those routes use Fictix-supplied management credentials. Console customers can use the company-key workflow above. # ============================================================ # https://fictix-docs.vercel.app/api/company # ============================================================ # Fictix Company API Read and operate synthetic companies with a company API key. Staging also supports one organization API key across all companies belonging to that organization. Webhook mutations require a Console bearer session; catalog routes are public. ## Servers - Production: `https://api.fictix.ai` - Staging: `https://api-staging.fictix.ai` ## Authentication - `X-API-Key`: Project-scoped runtime credential copied from Console → Company settings. Keep it server-side and use it only with /api/v1/orgs/{slug} routes. - `X-API-Key`: Staging only: an organization admin creates this key in Organization settings → Organization API keys. It accesses current and future companies in that organization. Select a company through the runtime slug. Keep the key server-side; it does not grant company creation, billing, membership, or key-management access. - `Authorization: Bearer`: Authenticated WorkOS org-user bearer session used by the signed-in Fictix Console and direct org-admin flows. Console sessions are managed by the Console and must not be copied or forwarded. ## catalog - **GET** `/api/v1/catalog/archetypes` — List Catalog Archetypes - **GET** `/api/v1/catalog/presets` — List Catalog Presets - **GET** `/api/v1/catalog/entities` — List Catalog Entities - **GET** `/api/v1/catalog/services` — List Catalog Services - **GET** `/api/v1/catalog/connector-capabilities` — List Catalog Connector Capabilities - **GET** `/api/v1/catalog/export-packs` — List Catalog Export Packs - **GET** `/api/v1/catalog/services/{service_id}/objects/{object_type}/schema` — Get Catalog Object Schema - **GET** `/api/v1/catalog/export-packs/qbo/schema` — Get Catalog Qbo Export Schema - **GET** `/api/v1/catalog/export-packs/{target}/schema` — Get Catalog Export Pack Schema ## organizations - **GET** `/api/v1/organizations/{org_id}/companies` — List Organization Companies ## orgs - **GET** `/api/v1/orgs/{slug}/data/{entity}` — Org Browse - **GET** `/api/v1/orgs/{slug}/export/{entity}` — Org Export - **GET** `/api/v1/orgs/{slug}/employees/{employee_id}/summary` — Org Employee Summary - **GET** `/api/v1/orgs/{slug}/export-pack/csv` — Org Export Generic Csv Pack - **GET** `/api/v1/orgs/{slug}/export-pack/qbo` — Org Export Qbo Pack - **GET** `/api/v1/orgs/{slug}/export-pack/qbo/schema` — Org Export Qbo Pack Schema - **GET** `/api/v1/orgs/{slug}/export-pack/hubspot` — Org Export Hubspot Pack - **GET** `/api/v1/orgs/{slug}/export-pack/hubspot/schema` — Org Export Hubspot Pack Schema - **GET** `/api/v1/orgs/{slug}/export-pack/asana` — Org Export Asana Pack - **GET** `/api/v1/orgs/{slug}/export-pack/asana/schema` — Org Export Asana Pack Schema - **GET** `/api/v1/orgs/{slug}/export-pack/{target}` — Org Export Captured Vendor Pack - **GET** `/api/v1/orgs/{slug}/export-pack/{target}/schema` — Org Export Captured Vendor Pack Schema - **GET** `/api/v1/orgs/{slug}/info` — Org Info - **GET** `/api/v1/orgs/{slug}/capabilities` — Org Capabilities - **POST** `/api/v1/orgs/{slug}/execute` — Org Execute ## native - **DELETE** `/api/v1/orgs/{slug}/native/{service}/{path}` — Native Endpoint - **PUT** `/api/v1/orgs/{slug}/native/{service}/{path}` — Native Endpoint - **PATCH** `/api/v1/orgs/{slug}/native/{service}/{path}` — Native Endpoint - **POST** `/api/v1/orgs/{slug}/native/{service}/{path}` — Native Endpoint - **GET** `/api/v1/orgs/{slug}/native/{service}/{path}` — Native Endpoint ## simulation - **POST** `/api/v1/orgs/{slug}/simulation/start` — Simulation Start - **POST** `/api/v1/orgs/{slug}/simulation/advance` — Simulation Advance - **POST** `/api/v1/orgs/{slug}/simulation/step` — Simulation Step - **POST** `/api/v1/orgs/{slug}/simulation/pause` — Simulation Pause - **POST** `/api/v1/orgs/{slug}/simulation/resume` — Simulation Resume - **GET** `/api/v1/orgs/{slug}/simulation/status` — Simulation Status - **PATCH** `/api/v1/orgs/{slug}/simulation/dials` — Simulation Dials - **POST** `/api/v1/orgs/{slug}/simulation/checkpoint` — Simulation Checkpoint - **POST** `/api/v1/orgs/{slug}/simulation/restore/{checkpoint_id}` — Simulation Restore - **GET** `/api/v1/orgs/{slug}/simulation/checkpoints` — Simulation Checkpoints ## events - **GET** `/api/v1/orgs/{slug}/events` — List Events - **GET** `/api/v1/orgs/{slug}/events/{event_id}` — Get Event ## scenarios - **GET** `/api/v1/orgs/{slug}/scenarios` — List Scenario Library - **POST** `/api/v1/orgs/{slug}/simulation/scenarios` — Inject Scenarios - **GET** `/api/v1/orgs/{slug}/simulation/manifest` — Get Manifest - **DELETE** `/api/v1/orgs/{slug}/simulation/scenarios/{scenario_id}` — Deactivate Scenario - **POST** `/api/v1/orgs/{slug}/scenarios/generate` — Generate Scenario ## test-matrices - **POST** `/api/v1/orgs/{slug}/test-matrices` — Create Matrix - **GET** `/api/v1/orgs/{slug}/test-matrices` — List Matrices - **GET** `/api/v1/orgs/{slug}/test-matrices/{matrix_id}` — Get Matrix - **DELETE** `/api/v1/orgs/{slug}/test-matrices/{matrix_id}` — Delete Matrix - **POST** `/api/v1/orgs/{slug}/test-runs` — Submit Test Run - **GET** `/api/v1/orgs/{slug}/test-runs` — List Test Runs - **GET** `/api/v1/orgs/{slug}/test-runs/{run_id}` — Get Test Run ## personas - **GET** `/api/v1/orgs/{slug}/personas` — List Personas - **POST** `/api/v1/orgs/{slug}/personas/{role}/chat` — Persona Chat - **POST** `/api/v1/orgs/{slug}/personas/{role}/action` — Persona Action - **GET** `/api/v1/orgs/{slug}/personas/{role}/history` — Persona History ## audit - **GET** `/api/v1/orgs/{slug}/audit/events` — Audit Events - **GET** `/api/v1/orgs/{slug}/audit/usage` — Audit Usage - **GET** `/api/v1/orgs/{slug}/audit/api-keys` — Audit Api Keys ## webhooks - **POST** `/api/v1/orgs/{slug}/webhooks` — Create Webhook - **GET** `/api/v1/orgs/{slug}/webhooks` — List Webhooks - **DELETE** `/api/v1/orgs/{slug}/webhooks/{webhook_id}` — Delete Webhook - **POST** `/api/v1/orgs/{slug}/webhooks/{webhook_id}/test` — Test Webhook # ============================================================ # https://fictix-docs.vercel.app/api/partner # ============================================================ # Fictix Partner API Create and control synthetic-company demo jobs. External server-to-server callers use the Fictix-supplied X-Admin-Key; signed-in Console and direct org-admin flows use an authenticated WorkOS bearer session. Normal Console customers do not need this API. ## Servers - Production: `https://api.fictix.ai` - Staging: `https://api-staging.fictix.ai` ## Authentication - `Authorization: Bearer`: Authenticated WorkOS org-user bearer session used by the signed-in Fictix Console and direct org-admin flows. Console sessions are managed by the Console and must not be copied or forwarded. - `X-Admin-Key`: Fictix-approved server-to-server management credential (`FICTIX_ADMIN_API_KEY`). It is not a self-service or frontend credential. ## demo-jobs - **GET** `/api/v1/demo-jobs` — List Demo Jobs - **POST** `/api/v1/demo-jobs` — Create Demo Job - **GET** `/api/v1/demo-jobs/{job_ref}` — Get Demo Job - **GET** `/api/v1/demo-jobs/{job_ref}/status` — Get Demo Job Status - **GET** `/api/v1/demo-jobs/{job_ref}/quality` — Get Demo Job Quality - **GET** `/api/v1/demo-jobs/{job_ref}/certificate` — Get Demo Job Certificate - **GET** `/api/v1/demo-jobs/{job_ref}/runtime-readiness` — Get Demo Job Runtime Readiness - **GET** `/api/v1/demo-jobs/{job_ref}/eval-pack-manifest` — Get Demo Job Eval Pack Manifest - **GET** `/api/v1/demo-jobs/{job_ref}/bundle` — Get Demo Job Bundle - **GET** `/api/v1/demo-jobs/{job_ref}/entities/{entity}` — Get Demo Job Entity - **GET** `/api/v1/demo-jobs/{job_ref}/entities/{entity}/export` — Export Demo Job Entity - **POST** `/api/v1/demo-jobs/{job_ref}/publish` — Publish Demo Job - **POST** `/api/v1/demo-jobs/{job_ref}/clone` — Clone Demo Job - **POST** `/api/v1/demo-jobs/{job_ref}/reset` — Reset Demo Job - **POST** `/api/v1/demo-jobs/{job_ref}/simulation/start` — Start Demo Job Simulation - **GET** `/api/v1/demo-jobs/{job_ref}/simulation/status` — Get Demo Job Simulation Status - **POST** `/api/v1/demo-jobs/{job_ref}/simulation/advance` — Advance Demo Job Simulation - **POST** `/api/v1/demo-jobs/{job_ref}/simulation/step` — Step Demo Job Simulation - **POST** `/api/v1/demo-jobs/{job_ref}/simulation/pause` — Pause Demo Job Simulation - **POST** `/api/v1/demo-jobs/{job_ref}/simulation/resume` — Resume Demo Job Simulation - **PATCH** `/api/v1/demo-jobs/{job_ref}/simulation/dials` — Patch Demo Job Simulation Dials - **POST** `/api/v1/demo-jobs/{job_ref}/simulation/checkpoint` — Checkpoint Demo Job Simulation - **GET** `/api/v1/demo-jobs/{job_ref}/simulation/checkpoints` — List Demo Job Simulation Checkpoints - **POST** `/api/v1/demo-jobs/{job_ref}/simulation/restore/{checkpoint_id}` — Restore Demo Job Simulation - **GET** `/api/v1/demo-jobs/{job_ref}/scenarios` — List Demo Job Scenarios - **POST** `/api/v1/demo-jobs/{job_ref}/scenarios/generate` — Generate Demo Job Scenario - **POST** `/api/v1/demo-jobs/{job_ref}/simulation/scenarios` — Inject Demo Job Scenarios - **GET** `/api/v1/demo-jobs/{job_ref}/simulation/manifest` — Get Demo Job Manifest - **DELETE** `/api/v1/demo-jobs/{job_ref}/simulation/scenarios/{scenario_id}` — Deactivate Demo Job Scenario - **GET** `/api/v1/demo-jobs/{job_ref}/events` — List Demo Job Events - **GET** `/api/v1/demo-jobs/{job_ref}/events/{event_id}` — Get Demo Job Event - **POST** `/api/v1/demo-jobs/{job_ref}/test-matrices` — Create Demo Job Matrix - **GET** `/api/v1/demo-jobs/{job_ref}/test-matrices` — List Demo Job Matrices - **GET** `/api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id}` — Get Demo Job Matrix - **DELETE** `/api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id}` — Delete Demo Job Matrix - **POST** `/api/v1/demo-jobs/{job_ref}/test-runs` — Submit Demo Job Test Run - **GET** `/api/v1/demo-jobs/{job_ref}/test-runs` — List Demo Job Test Runs - **GET** `/api/v1/demo-jobs/{job_ref}/test-runs/{run_id}` — Get Demo Job Test Run # ============================================================ # https://fictix-docs.vercel.app/api-reference/catalog # ============================================================ # Catalog Catalog endpoints are read-only discovery endpoints. Use them before creating a job to learn what Fictix can generate and how returned records will look. Base path: ```text /api/v1/catalog ``` For how catalog results map to project runtime calls, see the [Capability Matrix](/api-reference/capability-matrix). > **Note:** Authentication: none. Catalog endpoints are public discovery endpoints and > contain no organization, project, credential, or generated tenant data. ## List archetypes **GET** `/api/v1/catalog/archetypes` ```http GET /api/v1/catalog/archetypes ``` Returns supported company archetypes and key generation defaults. Response shape: ```json { "object": "catalog_collection", "contract_version": "2026-07-17", "collection": "archetypes", "count": 3, "items": [ { "id": "saas_b2b", "name": "Saas B2B", "customers_per_employee": 3.0, "payment_rate": 0.92, "monthly_churn_rate": 0.02 } ] } ``` Use this to populate archetype pickers or to validate user input before job creation. ## List quality presets **GET** `/api/v1/catalog/presets` ```http GET /api/v1/catalog/presets ``` Returns: - `clean` - `realistic` - `messy` - `stress_test` Response item: ```json { "id": "realistic", "name": "Realistic", "description": "Minor real-world defects without breaking core workflows.", "defect_rate": 0.05 } ``` ## List entities **GET** `/api/v1/catalog/entities` ```http GET /api/v1/catalog/entities ``` Returns the public entity families available through demo-job entity APIs. Use this endpoint to: - build entity browsers - choose loader order - validate export requests - show users what data is available ## List services **GET** `/api/v1/catalog/services` ```http GET /api/v1/catalog/services ``` Returns supported connector/service families and visible record types. Use this endpoint when presenting selected services in a create-job UI. ## List connector capabilities **GET** `/api/v1/catalog/connector-capabilities` ```http GET /api/v1/catalog/connector-capabilities ``` Query params: | Name | Type | Default | Description | | --- | --- | --- | --- | | `include_unsupported` | boolean | `false` | Include unsupported connector manifests. | Response includes: - total count - selectable count - unsupported count - capability metadata per connector ## List export packs **GET** `/api/v1/catalog/export-packs` ```http GET /api/v1/catalog/export-packs ``` Returns shipped file-oriented export packs such as QBO, HubSpot, Asana, and captured provider packs. Use this when the host product wants a file import path instead of a JSON API path. ## Get service object schema **GET** `/api/v1/catalog/services/{service_id}/objects/{object_type}/schema` ```http GET /api/v1/catalog/services/{service_id}/objects/{object_type}/schema ``` Examples: ```text GET /api/v1/catalog/services/qbo/objects/invoice/schema GET /api/v1/catalog/services/stripe/objects/customer/schema ``` Returns display columns for generic and native-shaped object views. ## Get export pack schema **GET** `/api/v1/catalog/export-packs/{target}/schema` ```http GET /api/v1/catalog/export-packs/qbo/schema GET /api/v1/catalog/export-packs/{target}/schema ``` Use schema endpoints to: - display available CSV files - validate import tooling - generate docs for export bundles - compare expected files against downloaded artifacts # ============================================================ # https://fictix-docs.vercel.app/api-reference/demo-jobs # ============================================================ # 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](https://console.fictix.ai) 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: ```text /api/v1/demo-jobs ``` For the stable response fields, contract version, and error envelope, see [Response Envelopes](/api-reference/response-envelopes) and the live [`/openapi.json`](https://api.fictix.ai/openapi.json). ## Create a demo job **POST** `/api/v1/demo-jobs` ```http POST /api/v1/demo-jobs ``` Headers: ```http Content-Type: application/json Idempotency-Key: X-Admin-Key: $FICTIX_ADMIN_API_KEY ``` > **Warning:** `/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: ```json { "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: ```json { "company": { "name": "Acme Logistics" }, "generation": { "employees": 20, "months_of_history": 6, "quality_preset": "clean" } } ``` ### Response ```json { "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` ```http 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: ```json { "object": "list", "contract_version": "2026-07-17", "count": 1, "jobs": [] } ``` ## Get demo job **GET** `/api/v1/demo-jobs/{job_ref}` ```http 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` ```http 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` ```http 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` ```http 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` ```http 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` ```http 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` ```http POST /api/v1/demo-jobs/{job_ref}/publish ``` Returns the updated job plus handoff metadata: ```json { "handoff": { "publish_id": "pub_abc123", "dashboard_url": "https://...", "staging_urls": {} } } ``` ## Clone **POST** `/api/v1/demo-jobs/{job_ref}/clone` ```http 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` ```http POST /api/v1/demo-jobs/{job_ref}/reset ``` Body: ```json { "confirm": true } ``` Returns: ```json { "object": "demo_job_reset", "contract_version": "2026-07-17", "status": "reset", "job": {} } ``` > **Warning:** Use reset carefully. Clone is safer for normal iteration because reset destroys > current generated state. # ============================================================ # https://fictix-docs.vercel.app/api-reference/entities-and-exports # ============================================================ # Entities and exports Entity endpoints return canonical Fictix data from one demo job. For a self-service Console-created company, start with the project runtime routes in [Runtime And Native Routes](/api-reference/runtime-native-routes). The normalized `/demo-jobs` entity surface is for approved control-plane integrations. ## Get a full bundle **GET** `/api/v1/demo-jobs/{job_ref}/bundle` ```http GET /api/v1/demo-jobs/{job_ref}/bundle ``` Query params: | Name | Type | Description | | --- | --- | --- | | `entity` | repeated string | Include only selected entity families. | | `include_empty` | boolean | Include empty entity families when resolving defaults. | | `max_records` | integer | Optional cap, bounded by server maximum. | Example: ```bash curl "$FICTIX_API_ORIGIN/api/v1/demo-jobs/$JOB_ID/bundle?entity=customers&entity=invoices" \ -H "X-Admin-Key: $FICTIX_ADMIN_API_KEY" ``` Response: ```json { "object": "demo_bundle", "contract_version": "2026-07-17", "job": {}, "lineage": {}, "included_entities": ["customers", "invoices"], "total_records": 1200, "record_limit_applied": 25000, "entities": { "customers": [], "invoices": [] } } ``` > **Note:** If the bundle exceeds the cap, Fictix returns `413`. Use entity paging or export > instead. ## Get entity page **GET** `/api/v1/demo-jobs/{job_ref}/entities/{entity}` ```http GET /api/v1/demo-jobs/{job_ref}/entities/{entity} ``` Query params: | Name | Type | Default | Description | | --- | --- | --- | --- | | `limit` | integer | `100` | Page size. | | `offset` | integer | `0` | Offset into entity list. | Example: ```bash curl "$FICTIX_API_ORIGIN/api/v1/demo-jobs/$JOB_ID/entities/customers?limit=100&offset=0" \ -H "X-Admin-Key: $FICTIX_ADMIN_API_KEY" ``` Response: ```json { "object": "demo_entity_page", "contract_version": "2026-07-17", "job_id": "8f3a2d11", "lineage": {}, "entity": "customers", "count": 1200, "limit": 100, "offset": 0, "data": [] } ``` ## Export entity **GET** `/api/v1/demo-jobs/{job_ref}/entities/{entity}/export` ```http GET /api/v1/demo-jobs/{job_ref}/entities/{entity}/export?format=json GET /api/v1/demo-jobs/{job_ref}/entities/{entity}/export?format=csv ``` JSON response: ```json { "object": "demo_entity_export", "contract_version": "2026-07-17", "job_id": "8f3a2d11", "lineage": {}, "entity": "invoices", "format": "json", "count": 200, "data": [] } ``` CSV response returns `text/csv` content. ## Supported entities Preferred canonical entity names: | Entity | Description | | --- | --- | | `accounts` | Chart of accounts. | | `bank_accounts` | Bank account records. | | `bank_transactions` | Bank ledger activity. | | `bills` | Accounts payable bills. | | `customers` | Customer master records. | | `customer_payments` | Customer receipts. | | `credit_memos` | Applied accounts receivable credits linked to invoices. | | `deals` | CRM deals/opportunities. | | `employees` | Employee records. | | `invoices` | Accounts receivable invoices. | | `items` | Product/service items. | | `journal_entries` | General ledger journal entries. | | `payroll_runs` | Payroll batches and pay-period output. | | `tasks` | Operational task records. | | `vendor_payments` | Vendor/bill payments. | | `vendors` | Vendor master records. | ASC clinical extensions are available only for companies created with `company.industry: "asc_surgical"`: | Entity | Description | | --- | --- | | `operating_rooms` | Operating and procedure rooms. | | `surgical_cases` | Scheduled synthetic surgical cases. | | `clinical_incidents` | Synthetic safety events and near misses with ground-truth fields. | | `dictation_scripts` | Spoken-style incident narrations with synthetic extraction answer keys. | The clinical pack contains no real patient data. For activation, record shape, scenario needles, and evaluation guidance, see [Healthcare / ASC Data](/concepts/clinical-asc). Aliases: | Alias | Canonical entity | | --- | --- | | `payments` | `customer_payments` | | `bill_payments` | `vendor_payments` | New integrations should use canonical names to avoid double-counting. ## Loader order For relational loading, use this order: 1. `accounts` 2. `bank_accounts` 3. `items` 4. `customers` 5. `vendors` 6. `employees` 7. `invoices` 8. `credit_memos` 9. `bills` 10. `customer_payments` 11. `vendor_payments` 12. `bank_transactions` 13. `journal_entries` 14. `deals` 15. `tasks` 16. `payroll_runs` For an ASC company, then load `operating_rooms`, `surgical_cases`, `clinical_incidents`, and `dictation_scripts` in that order. This order ensures reference targets exist before dependent records. # ============================================================ # https://fictix-docs.vercel.app/api-reference/simulation-scenarios-events # ============================================================ # Simulation, scenarios, and events Simulation endpoints let a generated company evolve over time and expose an event stream that another application can consume. Base path: ```text /api/v1/demo-jobs/{job_ref} ``` ## Start simulation **POST** `/api/v1/demo-jobs/{job_ref}/simulation/start` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/start ``` Body: ```json { "seed": 42, "start_date": "2026-01-01", "dials": { "chaos": 0.1, "fraud": 0.0 } } ``` If simulation exists, returns current state. ## Get simulation status **GET** `/api/v1/demo-jobs/{job_ref}/simulation/status` ```http GET /api/v1/demo-jobs/{job_ref}/simulation/status ``` Returns simulation clock, dial config, pause/resume state, and autonomous status. ## Advance simulation **POST** `/api/v1/demo-jobs/{job_ref}/simulation/advance` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/advance ``` Body: ```json { "days": 30, "hours": 0, "minutes": 0 } ``` Errors: - `400` when the delta is not positive - `400` when the delta exceeds the configured maximum advance window ## Step simulation **POST** `/api/v1/demo-jobs/{job_ref}/simulation/step` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/step ``` Runs one simulation step. ## Pause simulation **POST** `/api/v1/demo-jobs/{job_ref}/simulation/pause` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/pause ``` ## Resume simulation **POST** `/api/v1/demo-jobs/{job_ref}/simulation/resume` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/resume ``` ## Update dials **PATCH** `/api/v1/demo-jobs/{job_ref}/simulation/dials` ```http PATCH /api/v1/demo-jobs/{job_ref}/simulation/dials ``` Body: ```json { "trickle_rate": "10x", "fraud": 0.2, "chaos": 0.1 } ``` ## Create checkpoint **POST** `/api/v1/demo-jobs/{job_ref}/simulation/checkpoint` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/checkpoint ``` ## List checkpoints **GET** `/api/v1/demo-jobs/{job_ref}/simulation/checkpoints` ```http GET /api/v1/demo-jobs/{job_ref}/simulation/checkpoints ``` ## Restore checkpoint **POST** `/api/v1/demo-jobs/{job_ref}/simulation/restore/{checkpoint_id}` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/restore/{checkpoint_id} ``` ## List scenarios **GET** `/api/v1/demo-jobs/{job_ref}/scenarios` ```http GET /api/v1/demo-jobs/{job_ref}/scenarios ``` Returns scenario definitions available for the job/runtime. ## Generate scenario suggestions **POST** `/api/v1/demo-jobs/{job_ref}/scenarios/generate` ```http POST /api/v1/demo-jobs/{job_ref}/scenarios/generate ``` Use this to ask Fictix for scenario candidates appropriate to the company. ## Inject simulation scenarios **POST** `/api/v1/demo-jobs/{job_ref}/simulation/scenarios` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/scenarios ``` Body: ```json { "scenarios": [ { "id": "ghost-employee", "params": { "months": 1 } } ] } ``` Returns or updates planted truth for the simulation. ## Get scenario manifest **GET** `/api/v1/demo-jobs/{job_ref}/simulation/manifest` ```http GET /api/v1/demo-jobs/{job_ref}/simulation/manifest ``` > **Note:** The manifest describes planted truth. Use it to build eval harnesses and to > explain what the target application should have detected. ## Delete scenario **DELETE** `/api/v1/demo-jobs/{job_ref}/simulation/scenarios/{scenario_id}` ```http DELETE /api/v1/demo-jobs/{job_ref}/simulation/scenarios/{scenario_id} ``` Removes a scenario when supported by the backend state. ## List events **GET** `/api/v1/demo-jobs/{job_ref}/events` ```http GET /api/v1/demo-jobs/{job_ref}/events ``` Query params: | Name | Type | Description | | --- | --- | --- | | `limit` | integer | Max events to return. | | `cursor` | string | Cursor for pagination, where supported. | | `event_type` | string | Filter by event type, where supported. | ## Get event **GET** `/api/v1/demo-jobs/{job_ref}/events/{event_id}` ```http GET /api/v1/demo-jobs/{job_ref}/events/{event_id} ``` Use events to: - replay activity into another product - inspect what simulation generated - debug webhook delivery - build eval timelines # ============================================================ # https://fictix-docs.vercel.app/api-reference/test-matrices-runs # ============================================================ # Test matrices and runs Test matrices define what an application should detect. Test runs submit what the application actually detected. Fictix scores the submitted findings against planted truth. Base path: ```text /api/v1/demo-jobs/{job_ref} ``` ## Create test matrix **POST** `/api/v1/demo-jobs/{job_ref}/test-matrices` ```http POST /api/v1/demo-jobs/{job_ref}/test-matrices ``` Example body: ```json { "name": "Accounting QA", "scenarios": [ { "scenario_id": "ghost-employee", "severity": "critical", "required": true } ] } ``` Use a matrix to define the expected detection contract for a product workflow. ## List test matrices **GET** `/api/v1/demo-jobs/{job_ref}/test-matrices` ```http GET /api/v1/demo-jobs/{job_ref}/test-matrices ``` ## Get test matrix **GET** `/api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id}` ```http GET /api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id} ``` ## Delete test matrix **DELETE** `/api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id}` ```http DELETE /api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id} ``` ## Create test run **POST** `/api/v1/demo-jobs/{job_ref}/test-runs` ```http POST /api/v1/demo-jobs/{job_ref}/test-runs ``` Example body: ```json { "matrix_id": "tm_abc123", "detected": [ { "entity_type": "employee", "entity_id": "emp_ghost_001", "finding": "ghost employee" } ] } ``` Fictix compares submitted findings with the matrix and scenario truth. ## List test runs **GET** `/api/v1/demo-jobs/{job_ref}/test-runs` ```http GET /api/v1/demo-jobs/{job_ref}/test-runs ``` ## Get test run **GET** `/api/v1/demo-jobs/{job_ref}/test-runs/{run_id}` ```http GET /api/v1/demo-jobs/{job_ref}/test-runs/{run_id} ``` ## Evaluation pattern 1. Create a demo job. 2. Inject or generate scenarios. 3. Fetch the scenario manifest. 4. Load the company into the target application. 5. Run the target application or agent. 6. Convert the target application's findings into Fictix detected findings. 7. Submit a test run. 8. Store the score and verdict. ## Detected finding shape Detected findings should be specific enough to match planted truth: ```json { "entity_type": "vendor", "entity_id": "ven_123", "finding": "duplicate vendor" } ``` Avoid vague findings such as: ```json { "finding": "something is wrong" } ``` > **Note:** Specific findings produce better scoring and better user-facing verdicts. # ============================================================ # https://fictix-docs.vercel.app/api-reference/runtime-native-routes # ============================================================ # Runtime and native routes Staging also accepts [organization API keys](/get-started/organization-api-keys) on runtime routes that accept a company key. Each URL still selects one company by slug. Routes documented as requiring a Console bearer session retain that requirement. Runtime routes are the project-scoped APIs exposed for a generated company. Console customers use them to connect an existing company to their own app; approved server-to-server integrations use `/demo-jobs` when they need to create or control normalized jobs programmatically. For Console customers, runtime routes are the normal first integration surface: create/select a company in the [Fictix Console](https://console.fictix.ai), then copy its project key and base URL from **Connect to your builder** or **Company API keys**. See [Start In The Fictix Console](/get-started/console-onboarding). Runtime routes use: ```http X-API-Key: ``` Create a runtime key on the company's API page or under **Company API keys**. The full secret is shown at creation or rotation. The staging API page also provides a provider selector and a tested first request. Start with [Connect your software](/guides/connect-your-software). ## Runtime info **GET** `/api/v1/orgs/{slug}/info` ```http GET /api/v1/orgs/{slug}/info ``` Returns generated company metadata and runtime details. ## Capabilities **GET** `/api/v1/orgs/{slug}/capabilities` ```http GET /api/v1/orgs/{slug}/capabilities ``` Returns capability metadata available for the generated company. ## Execute capability **POST** `/api/v1/orgs/{slug}/execute` ```http POST /api/v1/orgs/{slug}/execute ``` Executes a named capability against the generated company runtime. Read capabilities need `read` and `execute` scopes; write capabilities need `write` and `execute`. The body's legacy `org_id` is the company project ID, not the workspace organization ID or company slug. Check `status` in the JSON response: an execution error may use HTTP 200. FCI provides derived financial summaries through this surface and retains preview status. It has no native route and is not selectable in the standard connector picker. Confirm capability availability for your company. See the [FCI example](/guides/connect-your-software#5-use-fci-through-execute). ## Browse canonical data **GET** `/api/v1/orgs/{slug}/data/{entity}` ```http GET /api/v1/orgs/{slug}/data/{entity} ``` Use this when you intentionally work with the org-scoped runtime instead of the partner demo-job surface. ## Export canonical data **GET** `/api/v1/orgs/{slug}/export/{entity}` ```http GET /api/v1/orgs/{slug}/export/{entity} ``` ## CSV ledger bundle **GET** `/api/v1/orgs/{slug}/export-pack/csv` ```http GET /api/v1/orgs/{slug}/export-pack/csv ``` Returns a zip archive containing canonical CSV ledgers and docs. Expected bundle behavior: - one zip file - all canonical ledgers - README - schema metadata - normalized line files where available - no duplicate `payments.csv` and `customer_payments.csv` - no duplicate `bill_payments.csv` and `vendor_payments.csv` ## Provider export packs **GET** `/api/v1/orgs/{slug}/export-pack/{target}` ```http GET /api/v1/orgs/{slug}/export-pack/qbo GET /api/v1/orgs/{slug}/export-pack/hubspot GET /api/v1/orgs/{slug}/export-pack/asana GET /api/v1/orgs/{slug}/export-pack/{target} ``` Schema routes: ```http GET /api/v1/orgs/{slug}/export-pack/qbo/schema GET /api/v1/orgs/{slug}/export-pack/hubspot/schema GET /api/v1/orgs/{slug}/export-pack/asana/schema GET /api/v1/orgs/{slug}/export-pack/{target}/schema ``` ## Native service routes Native routes emulate provider-shaped APIs for selected systems. Public catalog snapshots observed on October 28, 2026 list 60 entries in both staging and production. They mark 59 entries available/selectable with native support and one FCI execute preview as not selectable; all entries report approximate fidelity. These catalog values do not verify individual requests or promise strict upstream parity. The published first-request templates were tested in staging only. Use the [provider directory](/api-reference/provider-first-requests) for exact paths, POST bodies, content types, and catalog status, and the [workflow reference](/api-reference/provider-workflows) for staging-verified operations. Before calling a provider/object route, query the public [Capability Matrix](/api-reference/capability-matrix) and catalog object-schema endpoint. The company and selected services determine whether a path has `native`, `generic`, or no support. Native routes also require the `native.genome` entitlement. The Console shows the active plan and feature availability for the organization that owns the company. The routing pattern is: ```http /api/v1/orgs/{slug}/native/{service}/{provider_path} ``` `provider_path` can include API versions, account IDs, a GraphQL endpoint, an XML gateway, or a form endpoint. It is not a universal object CRUD path. For example: ```http GET /api/v1/orgs/{slug}/native/stripe/v1/customers GET /api/v1/orgs/{slug}/native/qbo/v3/company/test/query?query=SELECT%20*%20FROM%20Customer POST /api/v1/orgs/{slug}/native/monday/v2 POST /api/v1/orgs/{slug}/native/sage_intacct/ia/xml/xmlgw.phtml ``` Use the canonical provider ID `qbo` for QuickBooks requests. Native reads need `read`; native writes need `write`. Some reads use POST. Writes require a nonempty idempotency key. Send a fresh `Idempotency-Key` header for each operation, unless the provider workflow documents another header or body field for that route. Reuse the key only when retrying the same operation and payload. Inspect provider-level errors as well as HTTP status. ## Choosing demo jobs vs runtime routes Use `/demo-jobs` when: - you are creating companies - you want normalized data - you need the stable partner contract - you need quality/certificate/readiness metadata - you do not want to manage runtime API keys Use runtime routes when: - your product specifically needs `X-API-Key` isolation - you are testing provider-shaped API behavior - you need raw export packs - you are building internal tooling against generated companies ## Key lifecycle Project API keys are created, rotated, and revoked in the Console. They are scoped to a single generated company. If a key is exposed, rotate it in the Console and update your server-side secret before revoking the old key. > **Warning:** Never use an `X-Admin-Key` or a project key in frontend JavaScript. The Console > is the frontend entry point; runtime keys belong only in server-side workloads. # ============================================================ # https://fictix-docs.vercel.app/api-reference/response-envelopes # ============================================================ # Response envelopes Fictix returns generated business records, so entity-specific data can vary by company, selected services, and scenario. The partner API keeps the outer envelope stable while allowing the record payload to remain true to the generated company. The live schema is available at: ```text https://api.fictix.ai/openapi.json ``` Use the schema for mechanical route, request, authentication, and top-level response validation. Use the catalog endpoints to discover the fields and capabilities selected for a particular company. ## Contract version Partner response envelopes include a date-based contract version: ```json { "contract_version": "2026-07-17" } ``` Treat it as a string, not an integer. Store the version with any imported dataset, export, test run, or evaluation result. ## Demo job ```json { "object": "demo_job", "contract_version": "2026-07-17", "regeneration_required": false, "id": "8f3a2d11", "status": "ready", "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": {}, "dataset_summary": {}, "quality": {} } ``` | Field | Type | Meaning | | --- | --- | --- | | `object` | string | Stable resource type: `demo_job`. | | `contract_version` | string | Partner contract version for the response. | | `regeneration_required` | boolean | `true` when this company predates the current accounting contract and must be regenerated before a current-contract import. | | `id` | string | Demo-job/project identifier. | | `status` | string | `created`, `generating`, `ready`, `error`, or `unknown`. | | `links` | object | Relative links to the job's normalized resources. | | `company` | object | Generated company identity and summary. | | `runtime` | object | Runtime project id, slug, and handoff metadata. | | `dataset_summary` | object | Canonical record counts and materialization summary. | | `quality` | object | Validation summary appropriate for fast display. | > **Warning:** The `credentials.api_key` field can appear immediately after a control-plane > create/clone. It is a one-time server secret; do not log or return it to a > browser. Console-created companies use the Console's **Company API keys** flow > instead. ## Entity page ```json { "object": "demo_entity_page", "contract_version": "2026-07-17", "job_id": "8f3a2d11", "lineage": {}, "entity": "customers", "count": 1200, "limit": 100, "offset": 0, "data": [] } ``` > **Note:** `count` is the total for the requested canonical entity; `data` contains the > current page. Do not infer end-of-pagination from a short page alone—continue > until `offset + data.length >= count`. ## Catalog collection ```json { "object": "catalog_collection", "contract_version": "2026-07-17", "collection": "services", "count": 4, "items": [] } ``` Catalog endpoints are public and return no tenant data. They are the right source for available entities, connector capabilities, and export-pack schemas. ## Error envelopes Most errors include `detail`. The value may be text or a structured object: ```json { "detail": { "code": "project_dataset_limit_exceeded", "message": "Project dataset exceeds current blob storage safety budget" }, "request_id": "req_example" } ``` Create-time credit errors (`402`, and some `409`, `422`, and `429` responses) use a separate envelope: ```json { "error": "insufficient_credits", "message": "A valid credit run or sufficient credits are required." } ``` Write clients that tolerate both envelope families: ```ts type FictixDetailError = { detail: string | { code?: string; message?: string; [key: string]: unknown }; request_id?: string; }; type FictixCreditError = { error: string; message: string; }; type FictixError = FictixDetailError | FictixCreditError; ``` > **Warning:** Capture `request_id`, method, path, and safe job/entity identifiers when > escalating a problem. Never include credentials in support logs. ## CSV and JSON exports `format=json` returns a `demo_entity_export` JSON envelope. `format=csv` returns `text/csv` and includes lineage in response headers when available. Choose CSV for file loaders and JSON for API-based transforms; do not expect the CSV endpoint to wrap rows in the JSON envelope. Next: [Entities And Exports](/api-reference/entities-and-exports) and [Errors, Limits, And Idempotency](/api-reference/errors-limits-idempotency). # ============================================================ # https://fictix-docs.vercel.app/api-reference/capability-matrix # ============================================================ # Capability matrix Fictix is intentionally capability-driven: the available records and native-shaped routes depend on the generated company and selected services. > **Warning:** Do not hard-code a provider/object assumption from a marketing list. Discover > the current capability before issuing a runtime request. ## Discovery surface Catalog endpoints are public and are safe to call before you have a company key: | Need | Endpoint | | --- | --- | | Canonical entities and aliases | `GET /api/v1/catalog/entities` | | Selectable/unsupported connector status | `GET /api/v1/catalog/connector-capabilities` | | Service families and visible record types | `GET /api/v1/catalog/services` | | Object display/schema metadata | `GET /api/v1/catalog/services/{service_id}/objects/{object_type}/schema` | | File-oriented export packs | `GET /api/v1/catalog/export-packs` | ## Access matrix The [provider directory](/api-reference/provider-first-requests) contains first-request templates reviewed for the September 7, 2026 staging release and tested in staging only. Public catalog snapshots observed October 28, 2026 list 60 entries in both environments (59 marked available/selectable and one FCI preview); all 60 report approximate fidelity. These catalog reports do not verify provider request behavior or promise strict upstream parity. The [workflow reference](/api-reference/provider-workflows) documents supported staging operations and limits. Relay Financial is unavailable. FCI uses execute and remains marked as preview with `ui_selectable: false` in both catalogs. In the connector catalog, `status` and `ui_selectable` describe availability. `native_supported` and `contract_surfaces` identify the available transports. `fidelity`, `parity_status`, and `limitations` describe compatibility; these are separate from the object-schema labels below. Query the API origin you will use rather than assuming the production and staging catalogs match. | Surface | Purpose | Credential | Source of truth | | --- | --- | --- | --- | | Catalog | Discover entities, services, and schemas | None | `/api/v1/catalog/*` | | Normalized runtime data | Read one Console-created company's records | Project `X-API-Key` | `/api/v1/orgs/{slug}/data/{entity}` | | Runtime exports | Load JSON, CSV, and supported packs | Project `X-API-Key` plus applicable entitlement | `/api/v1/orgs/{slug}/export/*` | | Native-shaped runtime | Exercise selected provider-like APIs | Project `X-API-Key` plus `native.genome` entitlement | `/api/v1/orgs/{slug}/native/{service}/...` | | Execute transport | Call a named capability, including FCI | Runtime `X-API-Key` with `execute` plus `read` or `write` for the capability | `/api/v1/orgs/{slug}/execute` | | Console management | Create/control jobs in the signed-in Console | Console-managed WorkOS bearer session (not copyable) | `/api/v1/demo-jobs/*` | | Demo-job control plane | Create/control normalized jobs from an external integration | Fictix-supplied `FICTIX_ADMIN_API_KEY` / `X-Admin-Key` | `/api/v1/demo-jobs/*` | In staging, an organization `X-API-Key` can also use the normalized data, export, and native runtime rows above for companies owned by that organization. The same scopes and entitlements apply. Its company directory is `GET /api/v1/organizations/{org_id}/companies`; it does not grant Console or demo-job management access. See [One key for multiple companies](/get-started/organization-api-keys). ## Fidelity labels The catalog can mark a native object schema as one of these levels: | Label | Meaning | | --- | --- | | `native` | The object has provider-shaped fields documented by the catalog schema. | | `generic` | The object is available but uses Fictix's generic field projection. | | `unsupported` | The connector is not selectable for the requested product state. | > **Note:** Treat `native` as shape compatibility, not a claim that every upstream endpoint > or vendor side effect is implemented. Test the exact method/path your product > uses against a staging company before promoting your own integration. ## Recommended capability check **GET** `/api/v1/catalog/connector-capabilities` **GET** `/api/v1/catalog/services/{service_id}/objects/{object_type}/schema` ```bash curl -sS "https://api.fictix.ai/api/v1/catalog/connector-capabilities" | jq curl -sS "https://api.fictix.ai/api/v1/catalog/services/qbo/objects/invoice/schema" | jq ``` Then use the project-specific key issued through the [Fictix Console](https://console.fictix.ai) to make the runtime call: **GET** `/api/v1/orgs/{slug}/native/{service}/...` ```bash curl -sS \ "https://api.fictix.ai/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/qbo/invoice" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` ## Unsupported or partial behavior Build integrations to handle `400`, `404`, and `422` without assuming a missing resource is a transport outage. Use the capability/catalog response to decide whether to hide a feature, choose a generic projection, or use another service. For a product-ready integration, persist the catalog version and selected service set alongside the imported data. That makes later support and replay work deterministic. # ============================================================ # https://fictix-docs.vercel.app/api-reference/webhooks # ============================================================ # Webhooks Webhooks let a Fictix runtime notify your application when simulation, data, or test events occur. Use webhooks when your product needs to react to a generated company over time instead of polling event endpoints. Webhook management currently lives on the runtime org surface: ```text /api/v1/orgs/{slug}/webhooks ``` > **Note:** Start in the [Fictix Console](https://console.fictix.ai): the company owner can > confirm the project, plan entitlement, runtime key, and webhook configuration > there. > **Warning:** Keep subscription creation and test delivery server-side; never expose the > project key or any other credential in browser code. The `slug` is returned from the demo job resource at: ```json { "runtime": { "slug": "acme-logistics" } } ``` ## Entitlement Webhook subscriptions require the `webhooks` entitlement and currently require a plan with the `webhooks` feature enabled (Developer, Pro, or Enterprise in the default catalog). If the project is not entitled, the API returns `403`. ## Authentication Create, delete, and test are Console/org-admin actions. The signed-in Fictix Console manages a WorkOS-backed bearer session, and a direct authenticated WorkOS org administrator may use that session type for these operations: ```http Authorization: Bearer ``` The Console owns its session; it is not an external integration credential to source, copy, or forward. External server-to-server integrations use the Fictix-supplied `X-Admin-Key` on the demo-job control plane, not a copied or invented management bearer. Listing accepts either an authenticated WorkOS org-user bearer session or the project runtime key issued by the Console: ```http X-API-Key: ``` > **Warning:** Keep webhook management server-side. Do not expose the project runtime API key > or any other credential in browser code. ## Create a webhook **POST** `/api/v1/orgs/{slug}/webhooks` ```http POST /api/v1/orgs/{slug}/webhooks Content-Type: application/json Authorization: Bearer ``` Request: ```json { "url": "https://example.test/fictix/webhooks", "events": ["fictix.test", "invoice.issued"], "secret": "whsec_32_bytes_or_more" } ``` Fields: | Field | Type | Required | Notes | | --- | --- | --- | --- | | `url` | URL | Yes | HTTPS callback URL. The server validates callback safety before storing it. | | `events` | string array or null | No | `null` or omitted subscribes to all events. A provided list is capped to the first 50 event names. | | `secret` | string or null | No | Shared signing secret. Store a high-entropy value. Fictix does not return it later. | Response: ```json { "id": "0b96f8220f0c46c9a6c33c77", "company_id": "proj_123", "url": "https://example.test/fictix/webhooks", "events": ["fictix.test", "invoice.issued"], "has_secret": true, "active": true, "created_at": "2026-07-01T10:15:30.000000+00:00", "updated_at": "2026-07-01T10:15:30.000000+00:00" } ``` Secrets are never returned. The response uses `has_secret` so your UI can show whether signing is configured without leaking the secret. ## List webhooks **GET** `/api/v1/orgs/{slug}/webhooks` ```http GET /api/v1/orgs/{slug}/webhooks X-API-Key: ``` Response: ```json { "webhooks": [ { "id": "0b96f8220f0c46c9a6c33c77", "company_id": "proj_123", "url": "https://example.test/fictix/webhooks", "events": null, "has_secret": true, "active": true, "created_at": "2026-07-01T10:15:30.000000+00:00", "updated_at": "2026-07-01T10:15:30.000000+00:00" } ], "count": 1 } ``` `events: null` means the subscription receives every event type. ## Delete a webhook **DELETE** `/api/v1/orgs/{slug}/webhooks/{webhook_id}` ```http DELETE /api/v1/orgs/{slug}/webhooks/{webhook_id} Authorization: Bearer ``` Response: ```json { "deleted": true, "webhook_id": "0b96f8220f0c46c9a6c33c77" } ``` Deleting a webhook removes the subscription. It does not remove historical events from the Fictix event log. ## Send a test event **POST** `/api/v1/orgs/{slug}/webhooks/{webhook_id}/test` ```http POST /api/v1/orgs/{slug}/webhooks/{webhook_id}/test Content-Type: application/json Authorization: Bearer ``` Request: ```json { "event_type": "fictix.test" } ``` Response: ```json { "queued": true, "webhook_id": "0b96f8220f0c46c9a6c33c77", "event_type": "fictix.test", "url": "https://example.test/fictix/webhooks" } ``` The response means delivery was queued or started. Your receiver should still validate that the callback arrived. ## Delivery headers Webhook POST requests include: ```http Content-Type: application/json X-Fictix-Event: X-Fictix-Timestamp: X-Fictix-Signature: sha256= ``` `X-Fictix-Signature` is present only when the subscription has a `secret`. The signature is HMAC-SHA256 over the raw request body bytes using the secret stored on the subscription. ## Verify a signature Node example: ```js import crypto from "node:crypto"; export function verifyFictixWebhook({ rawBody, signature, secret }) { if (!signature || !signature.startsWith("sha256=")) { return false; } const expected = crypto .createHmac("sha256", secret) .update(rawBody) .digest("hex"); const received = signature.slice("sha256=".length); return crypto.timingSafeEqual( Buffer.from(received, "hex"), Buffer.from(expected, "hex") ); } ``` > **Warning:** Important implementation detail: verify against the raw body bytes, not a > JSON-reencoded object. Reencoding can change whitespace or key order and break > a valid signature. ## Payload shape Test payloads use this envelope: ```json { "id": "test_1720746930", "event_type": "fictix.test", "company_id": "proj_123", "data": { "message": "This is a test event from Fictix webhooks" }, "timestamp": "2026-07-11T10:15:30.000000+00:00" } ``` Persisted simulation and runtime events use the canonical envelope: ```json { "id": "evt_abc123", "company_id": "proj_123", "environment_id": null, "schema_version": "1.0", "event_type": "invoice.issued", "occurred_at": "2026-07-11T10:15:30.000000+00:00", "recorded_at": "2026-07-11T10:15:31.000000+00:00", "actor_kind": null, "actor_id": null, "entity_type": "invoice", "entity_id": "inv_123", "correlation_id": "corr_abc123", "parent_id": null, "data": {}, "mutation": null, "projection_refs": [], "audit_meta": null, "layer": 1 } ``` The stable fields are `id`, `company_id`, `schema_version`, `event_type`, `occurred_at`, and `data`. Treat all other fields as nullable or additive unless your integration needs them. Query the event stream with your project key at: **GET** `/api/v1/orgs/{slug}/events` ```http GET /api/v1/orgs/{slug}/events ``` Treat unknown event types as forward-compatible. Store the event, log it, and avoid failing the whole receiver. ## Delivery semantics Fictix delivers webhooks asynchronously. Current delivery behavior: - callback URLs are SSRF-guarded before storage and before delivery - event fanout is tenant scoped - active subscriptions are filtered by event type - deliveries are queued in a durable outbox - the worker claims due deliveries in batches - successful delivery is any `2xx` response - failed delivery is retried with backoff - stuck `sending` rows can be recovered by the worker Each worker delivery uses up to 3 immediate POST attempts. If that worker delivery fails, the durable outbox schedules another worker attempt with a 1, 2, 4, then 8 second backoff. The delivery becomes terminally failed after 5 worker attempts. A receiver must still be idempotent: network retries, the three immediate attempts, and worker recovery can produce duplicate callbacks. Receivers must be idempotent. Network retries and worker recovery can produce duplicate deliveries. Deduplicate by event id. ## Subscription limits Each org can have up to 20 active webhook subscriptions. If the limit is exceeded: ```http 422 Unprocessable Entity ``` ```json { "detail": "Maximum 20 webhook subscriptions per org" } ``` The `events` filter stores at most 50 event names per subscription. ## Receiver requirements Your receiver should: - respond within 10 seconds - return a `2xx` status only after accepting the event - verify `X-Fictix-Signature` when a secret is configured - deduplicate by event id - reject stale timestamps if your threat model requires replay protection - avoid fetching user-controlled URLs from the payload - log event id, type, company id, and delivery timestamp > **Warning:** Do not perform long-running ingestion synchronously inside the webhook > handler. Ack the event, enqueue work in your own system, and process it out of > band. ## Current event types `fictix.test` is the manual receiver test event. The current canonical financial event types include: ```text invoice.issued payment.received credit_note.issued refund.issued bill.received bill_payment.sent card_transaction.posted reimbursement.submitted bank_deposit.settled bank_withdrawal.settled bank_transfer.completed ``` Use exact values returned by your project's event stream when subscribing. Unknown event types may be added without a breaking API version, so retain and log them rather than rejecting an otherwise valid delivery. ## Troubleshooting No callbacks arrive: - confirm the project has the `webhooks` entitlement - confirm the subscription is active - confirm the event filter includes the emitted event type - send `fictix.test` - check that your callback URL is publicly reachable over HTTPS - check that your receiver returns a `2xx` response Signatures fail: - verify against raw request bytes - ensure the secret is the exact value used when the subscription was created - do not trim, parse, or reserialize the body before verification Duplicate callbacks: - expected under retry and recovery semantics - deduplicate by event id - make downstream processing idempotent # ============================================================ # https://fictix-docs.vercel.app/api-reference/errors-limits-idempotency # ============================================================ # Errors, limits, and idempotency This page documents error handling expectations for Fictix integrations. ## Error shape FastAPI routes may return simple string details: ```json { "detail": "No data. Generate first." } ``` or structured details: ```json { "detail": { "code": "project_dataset_limit_exceeded", "message": "Project dataset exceeds current blob storage safety budget" } } ``` Clients should handle both. `POST /api/v1/demo-jobs` can also return a credit envelope when admission or credit settlement fails: ```json { "error": "insufficient_credits", "message": "A valid credit run or sufficient credits are required." } ``` ## Common status codes | Status | Meaning | Client action | | --- | --- | --- | | `400` | Bad request, invalid delta, missing confirmation, no generated data | Fix request or create/generate data first. | | `401` | Missing or expired auth | Refresh credentials and retry once. | | `402` | No valid credit run or insufficient credits for a demo-job create | Fund or resolve the server-side credit run; do not retry blindly. | | `403` | Authenticated but not allowed | Check org membership, role, or credential scope. | | `404` | Job, project, entity, matrix, run, event, or checkpoint not found | Verify id and tenant scope. | | `409` | Idempotency conflict or active generation conflict | Use a new idempotency key or wait for active run. | | `413` | Bundle or dataset too large | Page entities or export per entity. | | `422` | Invalid request body, priced generation shape, or credit validation failure | Fix request fields or resolve credit state before retrying. | | `429` | Generation, rate, plan, or concurrency limit | Back off and retry after the applicable limit clears. | | `500` | Internal failure | Retry safely when idempotent; otherwise contact support with request id. | | `503` | Service not ready | Retry with backoff; check readiness. | ## Idempotency **POST** `/api/v1/demo-jobs` `POST /api/v1/demo-jobs` requires: ```http Idempotency-Key: ``` If a network failure occurs after the server creates a job, retry the exact same request with the exact same idempotency key. Fictix returns the cached response. If you send a different body with the same key, Fictix returns: ```http 409 Conflict ``` ### Native writes Native writes require a nonempty idempotency key. Send `Idempotency-Key` unless the [provider workflow](/api-reference/provider-workflows) documents another header or body field for that route. Give each new operation a new key; reuse the original key and payload when retrying it. Read operations, including POST requests modeled as reads, do not require a native-write idempotency key. For Mercury recipient creation (`POST /api/v1/recipients`), send the key in the `Idempotency-Key` header. The recipient JSON does not accept `idempotencyKey`; that body field is supported for Mercury payment and transfer requests. ## Bundle limit **GET** `/api/v1/demo-jobs/{job_ref}/bundle` `GET /api/v1/demo-jobs/{job_ref}/bundle` is intentionally capped. When requested entity records exceed the cap: ```json { "detail": "Bundle too large for one response. Requested 30001 records across 15 entities, which exceeds the partner bundle cap of 25000. Use the entity or export endpoints instead." } ``` > **Warning:** The active cap is deployment-configurable. Do not parse the number from an > error message; handle `413` by paging or exporting instead. Switch to: ```text GET /api/v1/demo-jobs/{job_ref}/entities/{entity} GET /api/v1/demo-jobs/{job_ref}/entities/{entity}/export ``` ## Generation budget limit Large generation shapes can be rejected before expensive work starts. The error explains: - weighted load - estimated records - quality preset - connector count - budget - safe employees/months suggestions To fix: - lower `generation.employees` - lower `generation.months_of_history` - reduce `selected_services` - choose a less aggressive quality preset - create multiple smaller jobs ## Retry guidance Safe to retry with same idempotency key: - network timeout during `POST /demo-jobs` - `503` - `500` before receiving a response body Safe to retry without idempotency key: - `GET` requests - `DELETE` after verifying semantics are acceptable > **Warning:** Do not blindly retry: > > - reset > - clone > - publish if your client cannot tolerate duplicate side effects > - test run submission unless your client can dedupe by matrix/run id ## Request IDs When available, capture response headers such as request id or trace id from errors and include them in support tickets. Recommended client logging: - request method - path, without secrets - status code - job id - entity - idempotency key hash, not raw key - response request id header - response body detail # ============================================================ # https://fictix-docs.vercel.app/api-reference/provider-first-requests # ============================================================ # Provider first requests Public catalog snapshots observed 2026-10-28; first-request templates reviewed 2026-09-07. - **staging** (`https://api-staging.fictix.ai/api/v1/catalog/connector-capabilities`, observed 2026-10-28): 60 catalog entries, 60 listed as available/preview, and 59 marked selectable; status histogram 59 available, 1 preview; fidelity histogram 60 approximate. - **production** (`https://api.fictix.ai/api/v1/catalog/connector-capabilities`, observed 2026-10-28): 60 catalog entries, 60 listed as available/preview, and 59 marked selectable; status histogram 59 available, 1 preview; fidelity histogram 60 approximate. These status and fidelity values are public catalog reports. They do not show that each provider request succeeded, establish production request behavior, or promise strict upstream API parity. The request templates below were tested in staging only. Plan entitlements and company selection still apply. FCI catalog status: staging: preview, not selectable; production: preview, not selectable. It uses `execute` and is not selectable in the standard connector picker. Check capability availability for your company before using its preview request. Start with [Connect your software](/guides/connect-your-software) for keys, SDK base URLs, scopes, and error handling. Read [Provider workflows](/api-reference/provider-workflows) for supported writes, filters, pagination, and limits. [Download all first requests as JSON](/provider-first-requests.json). The file contains public request templates and source release IDs; it contains no keys. ## Request base and authentication Append each table path to `/api/v1/orgs/{slug}/` on your chosen API origin. Use `https://api-staging.fictix.ai` for staging and `https://api.fictix.ai` for production. Send the matching company or organization key as `X-API-Key`. Every first request needs `read`; FCI additionally needs `execute`. For a GET, replace the path below with the one from the table: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/stripe/v1/customers" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` POST read bodies and their content types are included below the table. These requests do not require a native-write idempotency key. Replace the two FCI body placeholders with the selected company project ID and a unique request ID. ## Provider directory | Provider / ID | Staging-tested first request | Public catalog report | | --- | --- | --- | | BILL / `billcom` | `GET native/billcom/v3/vendors` | staging: available, selectable; production: available, selectable | | Expensify / `expensify` | `POST native/expensify/Integration-Server/ExpensifyIntegrations` ([body](#expensify-request)) | staging: available, selectable; production: available, selectable | | FreshBooks / `freshbooks` | `GET native/freshbooks/accounting/account/FICTIX/users/clients` | staging: available, selectable; production: available, selectable | | QuickBooks Online / `qbo` | `GET native/qbo/v3/company/test/query?query=SELECT%20*%20FROM%20Customer` | staging: available, selectable; production: available, selectable | | Xero / `xero` | `GET native/xero/api.xro/2.0/Contacts` | staging: available, selectable; production: available, selectable | | Zoho Books / `zoho_books` | `GET native/zoho_books/books/v3/contacts?organization_id=100000000000001` | staging: available, selectable; production: available, selectable | | Chase / `chase` | `GET native/chase/embedded/v1/accounts` | staging: available, selectable; production: available, selectable | | Mercury / `mercury` | `GET native/mercury/accounts` | staging: available, selectable; production: available, selectable | | Plaid / `plaid` | `POST native/plaid/transactions/get` ([body](#plaid-request)) | staging: available, selectable; production: available, selectable | | Wise Business / `wise_business` | `GET native/wise_business/2026Q3/profiles` | staging: available, selectable; production: available, selectable | | Close / `close` | `GET native/close/api/v1/lead/` | staging: available, selectable; production: available, selectable | | HubSpot / `hubspot` | `GET native/hubspot/crm/v3/objects/contacts` | staging: available, selectable; production: available, selectable | | Pipedrive / `pipedrive` | `GET native/pipedrive/api/v2/persons` | staging: available, selectable; production: available, selectable | | Salesforce / `salesforce` | `GET native/salesforce/services/data/v66.0/query?q=SELECT%20Id%2CName%20FROM%20Account` | staging: available, selectable; production: available, selectable | | Brex / `brex` | `GET native/brex/v2/accounts/cash` | staging: available, selectable; production: available, selectable | | Ramp / `ramp` | `GET native/ramp/developer/v1/transactions` | staging: available, selectable; production: available, selectable | | Shopify / `shopify` | `GET native/shopify/admin/api/2025-10/customers.json` | staging: available, selectable; production: available, selectable | | Gmail / `gmail` | `GET native/gmail/gmail/v1/users/me/messages` | staging: available, selectable; production: available, selectable | | Outlook / `outlook` | `GET native/outlook/v1.0/me/events` | staging: available, selectable; production: available, selectable | | Slack / `slack` | `GET native/slack/api/users.list` | staging: available, selectable; production: available, selectable | | GitHub / `github` | `GET native/github/user/repos` | staging: available, selectable; production: available, selectable | | DocuSign / `docusign` | `GET native/docusign/oauth/userinfo` | staging: available, selectable; production: available, selectable | | NetSuite / `netsuite` | `GET native/netsuite/services/rest/record/v1/customer` | staging: available, selectable; production: available, selectable | | SAP / `sap` | `GET native/sap/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner` | staging: available, selectable; production: available, selectable | | Sage Intacct / `sage_intacct` | `POST native/sage_intacct/ia/xml/xmlgw.phtml` ([body](#sage-intacct-request)) | staging: available, selectable; production: available, selectable | | Dropbox / `dropbox` | `POST native/dropbox/2/files/list_folder` ([body](#dropbox-request)) | staging: available, selectable; production: available, selectable | | Google Drive / `google_drive` | `GET native/google_drive/drive/v3/files` | staging: available, selectable; production: available, selectable | | OneDrive / `onedrive` | `GET native/onedrive/v1.0/me/drive/root/children` | staging: available, selectable; production: available, selectable | | FCI / `fci` | `POST execute` ([body](#fci-request)) | staging: preview, not selectable; production: preview, not selectable | | ADP / `adp` | `GET native/adp/hr/v2/workers` | staging: available, selectable; production: available, selectable | | BambooHR / `bamboohr` | `GET native/bamboohr/api/v1/employees` | staging: available, selectable; production: available, selectable | | Deel / `deel` | `GET native/deel/rest/people` | staging: available, selectable; production: available, selectable | | Justworks / `justworks` | `GET native/justworks/v1/members` | staging: available, selectable; production: available, selectable | | Rippling / `rippling` | `GET native/rippling/workers/` | staging: available, selectable; production: available, selectable | | TriNet HR Platform / `trinet` | `GET native/trinet/core/people` | staging: available, selectable; production: available, selectable | | Workday / `workday` | `GET native/workday/staffing/v7/workers` | staging: available, selectable; production: available, selectable | | Google Ads / `google_ads` | `GET native/google_ads/v25/customers:listAccessibleCustomers` | staging: available, selectable; production: available, selectable | | Google Analytics 4 / `google_analytics_4` | `GET native/google_analytics_4/v1beta/properties/1000000001/metadata` | staging: available, selectable; production: available, selectable | | Klaviyo / `klaviyo` | `GET native/klaviyo/api/profiles` | staging: available, selectable; production: available, selectable | | Mailchimp / `mailchimp` | `GET native/mailchimp/3.0/lists` | staging: available, selectable; production: available, selectable | | Meta Ads / `meta_ads` | `GET native/meta_ads/v26.0/me/adaccounts` | staging: available, selectable; production: available, selectable | | Airtable / `airtable` | `GET native/airtable/v0/meta/bases` | staging: available, selectable; production: available, selectable | | Adyen / `adyen` | `POST native/adyen/v72/paymentMethods` ([body](#adyen-request)) | staging: available, selectable; production: available, selectable | | Chargebee / `chargebee` | `GET native/chargebee/api/v2/customers` | staging: available, selectable; production: available, selectable | | Paddle / `paddle` | `GET native/paddle/customers` | staging: available, selectable; production: available, selectable | | PayPal / `paypal` | `GET native/paypal/v1/catalogs/products` | staging: available, selectable; production: available, selectable | | Recurly / `recurly` | `GET native/recurly/accounts` | staging: available, selectable; production: available, selectable | | Square / `square` | `GET native/square/v2/customers` | staging: available, selectable; production: available, selectable | | Stripe / `stripe` | `GET native/stripe/v1/customers` | staging: available, selectable; production: available, selectable | | Gusto / `gusto` | `GET native/gusto/v1/employees` | staging: available, selectable; production: available, selectable | | Excel / `excel` | `GET native/excel/v1.0/me/drive/items/fictix-finance-workbook/workbook/worksheets` | staging: available, selectable; production: available, selectable | | Asana / `asana` | `GET native/asana/api/1.0/tasks` | staging: available, selectable; production: available, selectable | | ClickUp / `clickup` | `GET native/clickup/api/v2/team` | staging: available, selectable; production: available, selectable | | Linear / `linear` | `POST native/linear/graphql` ([body](#linear-request)) | staging: available, selectable; production: available, selectable | | Monday / `monday` | `POST native/monday/v2` ([body](#monday-request)) | staging: available, selectable; production: available, selectable | | Freshdesk / `freshdesk` | `GET native/freshdesk/api/v2/contacts` | staging: available, selectable; production: available, selectable | | Front / `front` | `GET native/front/contacts` | staging: available, selectable; production: available, selectable | | Help Scout / `help_scout` | `GET native/help_scout/v2/customers` | staging: available, selectable; production: available, selectable | | Intercom / `intercom` | `GET native/intercom/contacts` | staging: available, selectable; production: available, selectable | | Zendesk / `zendesk` | `GET native/zendesk/api/v2/users` | staging: available, selectable; production: available, selectable | Relay Financial is unavailable: a direct API contract has not been implemented. FCI uses `execute`. TriNet means **HR Platform (formerly Zenefits)**; it does not describe TriNet PEO. ## POST read bodies The examples use the company-key variables from the connection guide. If you use an organization key, substitute `$FICTIX_ORGANIZATION_API_KEY` in the header. ### Expensify request Expensify: send `application/x-www-form-urlencoded`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/expensify/Integration-Server/ExpensifyIntegrations" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-raw 'requestJobDescription=%7B%22type%22%3A%22get%22%2C%22inputSettings%22%3A%7B%22type%22%3A%22policyList%22%7D%7D' ``` ### Plaid request Plaid: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/plaid/transactions/get" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{}' ``` ### Sage Intacct request Sage Intacct: send `application/xml`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/sage_intacct/ia/xml/xmlgw.phtml" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/xml" \ --data-raw 'fictix-sdkfirst-readtrue3.0CUSTOMER*10' ``` ### Dropbox request Dropbox: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/dropbox/2/files/list_folder" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"path":""}' ``` ### FCI request FCI: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/execute" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"org_id":"replace_with_company_project_id","user_id":"fictix-ui","turn_id":"replace_with_unique_request_id","capability_key":"fci.cash","args":{}}' ``` ### Adyen request Adyen: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/adyen/v72/paymentMethods" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"merchantAccount":"FictixDemo"}' ``` ### Linear request Linear: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/linear/graphql" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"query":"query { teams { nodes { id name } } }"}' ``` ### Monday request Monday: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/monday/v2" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"query":"query { boards { id name } }"}' ``` ## Check application status An HTTP 200 alone is insufficient for GraphQL, Sage Intacct, Expensify, and execute responses. Inspect GraphQL `errors`, XML operation statuses, Expensify `responseCode`, or execute `status` before using the returned data. See the connection guide for scope and error handling. # ============================================================ # https://fictix-docs.vercel.app/api-reference/provider-workflows # ============================================================ # Provider workflows > **Staging release:** The workflows on this page were reviewed for the > 60-entry staging catalog on September 7, 2026 and verified in staging. Public > catalog snapshots observed October 28, 2026 list 60 entries in both staging > and production (59 marked available/selectable and one FCI preview). Catalog > status does not verify production requests or establish strict API parity. > Check each provider in the > [first-request directory](/api-reference/provider-first-requests) and query > your environment's catalog before connecting. Start with [Connect your software](/guides/connect-your-software) for API keys, base URLs, SDK configuration, and response handling. The directory provides the request templates tested in staging and the public catalog report for each environment, including a downloadable JSON file. The production catalog report does not mean these request templates were tested in production. Relay Financial is unavailable until a direct API contract is implemented. These are synthetic workflows with documented limits. The public catalogs report 60 entries each: 59 marked available/selectable and one FCI execute preview. These are catalog annotations, not a claim that all provider routes work in both environments. FCI is not selectable in the standard connector picker; confirm capability availability for your company before using it. Use an organization runtime API key for companies in your workspace, or a company runtime API key for one company. Send it as `X-API-Key`. Native reads need `read`; native writes need `write`; execute reads need `execute` and `read`, while execute writes need `execute` and `write`. These keys authenticate to Fictix. The Chase `access_token` argument is optional and is not needed for synthetic banking operations. Discover the current runtime catalog at `/api/v1/catalog/connector-capabilities` and export packs at `/api/v1/catalog/export-packs`. A connector's availability does not mean that every endpoint of the upstream vendor is implemented. ## Native routes Append the provider path to: ```text /api/v1/orgs/{company_slug}/native/{provider} ``` | Providers | Workflow covered by generated-company verification | | --- | --- | | Airtable | List bases; create and retrieve a task record | | Asana | List tasks; create and retrieve a task | | BILL | List vendors; create and retrieve a vendor | | Chargebee | Read customers, invoices and transactions; manage customers, items, prices and subscription state | | ClickUp | Discover workspaces and lists; create, update, comment on and delete tasks | | DocuSign | Create, send and void envelopes; update recipients and download documents | | Dropbox, OneDrive | Read generated finance files; upload/download exact bytes and manage files/folders | | Brex | Cash accounts and transactions; manage virtual cards and vendor ACH accounts; post and retrieve transfers | | Chase | Read accounts and balances; manage recipients; post ACH, domestic wires and internal transfers | | Mercury | Manage payment recipients; post ACH, domestic wires and internal transfers; read ledger entries and update notes | | Wise Business | Discover a business profile and USD balance; create recipients and quotes; create, retrieve and cancel unfunded transfers | | Monday | Query boards and users; manage items, column values, groups and comments with GraphQL | | Excel | List worksheets; write and retrieve cell values | | Rippling | Read workers and users; create and update departments; create, update and delete teams | | BambooHR | Discover employees and field metadata; create and update personnel; filter and page through the roster | | Expensify | Discover and create policies; create draft expenses and reports; export and download report contents | | Gmail | Read, send and modify messages; manage drafts, labels and threads; retrieve raw MIME, attachments and history | | Google Drive | List files; create and retrieve a file | | Google Ads | Discover a test account; create, query, update and remove shared budgets and Search campaigns through v25 REST | | Google Analytics 4 | Validate and collect test web events; query their counts, users and page views with the Data API | | Meta Ads | Discover an ad account; create, retrieve, update, archive and delete campaigns with daily budgets | | Gusto | List employees; create and retrieve an employee | | HubSpot | List contacts; create and retrieve a contact | | NetSuite | List customers; create and retrieve a customer | | Outlook | List events; create and retrieve an event | | Pipedrive | Read and manage persons, organizations, deals, activities and products | | Plaid | Read transactions; refresh and retrieve the resulting event | | QuickBooks, Shopify, Square, Stripe | List customers; create and retrieve a customer | | Slack | Read the generated member directory through `api/users.list` | | Ramp | Read transactions; create and retrieve a card | | Salesforce | Query and manage CRM sObjects; compose requests with references and rollback | | Sage Intacct | XML queries and pagination; create, update and delete customers, vendors and GL accounts | | Xero | List contacts; create and retrieve a contact | | Zendesk | Manage users, organizations and tickets; append and retrieve ticket comments | Native writes require a nonempty idempotency key. Send a fresh `Idempotency-Key` header for each operation unless its workflow documents another header or body field. Reuse the key only when retrying the same operation and payload. ### Rippling Use `/api/v1/orgs/{slug}/native/rippling` as the REST base URL and send `Rippling-Api-Version: 2024-08-01` (also the default). `GET /workers/` and `GET /users/` read generated personnel; `GET /workers/{id}/` and `GET /users/{id}/` retrieve one record. Worker and user IDs remain stable and are distinct. Supported worker fields include status, work email, title, start/end dates and references to users, managers and departments. `GET /departments/` derives the initial departments from canonical employee labels. Create with `POST /departments/` and update with `PATCH /departments/{id}/`; both require `name`. An optional `parent_id` must reference an existing department, and `reference_code` persists. Renaming a department updates matching canonical employee department labels while keeping its Rippling ID. `GET /teams/`, `POST /teams/`, `GET /teams/{id}/`, `PATCH /teams/{id}/` and `DELETE /teams/{id}/` provide persistent team management. Team creation requires a name; updates change only submitted name/parent fields. Group names are unique within each resource. Parents must exist, cycles are rejected, and a team with children cannot be deleted. Writes require an `Idempotency-Key` and leave payroll and cash records unchanged. Lists accept `limit` (1–100; default 50), `cursor`, and `order_by` using `id`, `created_at` or `updated_at`, comma-separated with optional `asc`/`desc`. Follow the returned `next_link`; the terminal page omits it. Cursors are scoped to the company, resource and query. Workers additionally accept one `filter` comparison on status, work_email, user_id, created_at or updated_at. String fields use quoted `eq`/`ne`; timestamps also support `gt`, `ge`, `lt` and `le`. For example, `filter=status eq 'ACTIVE'` returns active workers. Compound predicates and unsupported fields return explicit errors. Expand supported relations using `expand=user,department` on workers, `expand=parent` on departments/teams, or two levels such as `expand=manager.user,department.parent`. The supported worker relations are user, manager, department and teams; generated team membership is empty. Hiring/termination writes, team-assignment writes, payroll, benefits, compensation, custom objects, permission administration and other API versions are outside this workflow. Missing optional fields are omitted. Response and request fixtures come from [Rippling's published OpenAPI](https://developer.rippling.com/docs/rest-beta.yaml). The official `@rippling/rippling-sdk` client can use this base URL and a Fictix `X-API-Key` header; supply `Idempotency-Key` on each write. ### BambooHR Use JSON requests under `/api/v1/orgs/{slug}/native/bamboohr/api/v1` with a Fictix runtime key. `GET /employees` lists the generated company roster; `GET /employees/directory` returns the published directory and its fieldset. `GET /meta/fields` identifies supported aliases and their synthetic numeric IDs. `GET /employees/{id}` returns only `id` unless `fields=firstName,workEmail,...` is supplied. Numeric field IDs from the metadata also work. Employee internal IDs are immutable and distinct from editable `employeeNumber` values. An integration key is not bound to an employee, so `GET /employees/0` returns `{"id":"0"}`. Create employees with `POST /employees` and at least `firstName` and `lastName`. The response is 201 with the new employee and a `Location` header. Update only submitted fields with `POST /employees/{id}`. Writable fields are first/last/ preferred name, work/home email and phone, mobile phone, employee number, department, division, location, job title, hire date and Active/Inactive status. Work email and employee number must be unique within the BambooHR roster. Writes require an `Idempotency-Key`. Personnel changes remain visible through canonical company records; they do not create payroll or cash transactions. `DELETE /employees/{id}` hides that BambooHR resource and returns 204, preserving shared personnel and financial history used by other integrations. The roster accepts `page[limit]` (1–2500), `page[after]` or `page[before]` using returned cursors and links. Filters use the comma-separated `filter[ids]` form, `filter[status]=active|inactive`, or case-insensitive substring matching for first/last name, job title name, work email, department/division/location name and employee number. Multiple filters combine with AND. Sort on employeeId, firstName, lastName, preferredName, jobTitleName or status, comma-separated with `-` for descending order. Cursors are bound to the company and query; changing or deleting the boundary employee invalidates that cursor. Additional roster fields use `fields=workEmail,departmentName,...` and have different aliases from single-employee reads. Use the documented comma-separated query forms. Unknown requested read fields are omitted. Unsupported write fields and query forms are rejected. Directory sharing is fixed for the synthetic company; `onlyCurrent=false` includes future hires, while inactive employees stay hidden. An empty directory returns 404. Request `Accept: application/json`; XML, custom fields, photos, payroll, benefits, effective-dated job/compensation history and permission administration are outside this workflow. Endpoint fixtures are extracted from [BambooHR's published OpenAPI](https://openapi.bamboohr.io/main/latest/docs/openapi/public-openapi.yaml). ### Expensify Send every job as `POST` to `/api/v1/orgs/{slug}/native/expensify/Integration-Server/ExpensifyIntegrations`. Use `Content-Type: application/x-www-form-urlencoded` and a `requestJobDescription` form field containing the job JSON. Plain JSON request bodies are unsupported. For example, a policy discovery job is: ```json {"type":"get","inputSettings":{"type":"policyList"}} ``` Authenticate with a Fictix runtime key in `X-API-Key`. A vendor `credentials` object may contain synthetic `partnerUserID` and `partnerUserSecret` strings for client compatibility; it is discarded before execution and persistence. Fictix does not authenticate against an Expensify account. Read scopes authorize `get` and `download` jobs. `create` and `file` jobs require `write` and a fresh `Idempotency-Key`. Retrying the same job with that key returns its saved result. Policy discovery returns one company policy plus any policies you create. Use `type: "create"` with `inputSettings.type: "policy"`, `policyName` and optional `plan: "team"` or `"corporate"` to create another. The result contains `responseCode: 200`, `policyID` and `policyName`. For policy details, use a `get` job with `inputSettings.type: "policy"`, a `policyIDList` array and a `fields` array. Supported fields are `employees`, `categories`, `reportFields`, `tags` and `tax`. Membership comes from the generated company's employees; the first employee is the synthetic policy owner. `policyList` also accepts `userEmail` and `adminOnly`. User emails must belong to that company. Categories, tags and tax start empty; the title is the only report field. Policy membership and configuration updates are unsupported. A report creation job uses `inputSettings.type: "report"`, `policyID`, a generated `employeeEmail`, `report: {"title": "Travel"}` and an `expenses` array. Each expense contains `date` in `YYYY-MM-DD`, `merchant`, `currency: "USD"` and positive integer `amount` in cents. The report stays `Open` / `Draft`. A standalone expense creation job uses `inputSettings.type: "expenses"`, `employeeEmail` and `transactionList`. These rows use `created` for their date and may also include `reportID`, `policyID`, `externalID`, `category`, `tag`, `billable`, `reimbursable` and `comment`. A report must belong to that employee; any supplied policy must agree with the report. The result includes string `transactionID` values and integer-cent amounts. `externalID` is an export label; use `Idempotency-Key` for retries. Each report or expense batch is atomic and contains at most 100 expenses. These writes do not reimburse employees, debit cash, allocate payments or post journal entries. To export reports, send a `file` job with `onReceive: {"immediateResponse": ["returnRandomFileName"]}`, `inputSettings.type: "combinedReportData"`, `inputSettings.filters.reportIDList` as comma-separated report IDs, and `outputSettings.fileExtension: "csv"` or `"txt"`. Pass a separate `template` form field using the documented FreeMarker subset, for example: ```text Report,Merchant,Amount <#list reports as report> <#list report.transactionList as expense> ${report.reportID},${expense.merchant},${expense.amount} ``` Supported constructs are report and expense loops, `addHeader` conditions, scalar field interpolation, whitespace trim directives, division by 100, `?string("0.00")`, boolean `?c` / `?string`, and string `?replace`. Use the template's quoting and replacement syntax when fields may contain CSV commas, quotes or newlines. Other expressions and directives fail explicitly. Templates are limited to 8 KiB, exports to 100 reports and output to 1 MiB. The export response is a plain filename. Send a `download` job with that `fileName` and optional `fileSystem: "integrationServer"` to retrieve the exact saved UTF-8 CSV or text bytes. Downloads remain unchanged when reports later receive new expenses. Filenames identify company-local stored exports. Business failures return `responseCode` and `responseMessage` in a JSON body, so clients must inspect `responseCode` even when HTTP is 200. Malformed transport bodies and Fictix authentication failures use HTTP errors. FX, tax, approvals, custom report fields, arbitrary FreeMarker, reconciliation, reimbursements, SFTP, email actions and webhooks are unsupported. The contract follows [Expensify's job reference](https://integrations.expensify.com/Integration-Server/doc/index.html) and [report template reference](https://integrations.expensify.com/Integration-Server/doc/export_report_template.html). ### Wise Business Use `/api/v1/orgs/{slug}/native/wise_business/2026Q3` with a Fictix runtime key in `X-API-Key`. This contract follows Wise's published 2026Q3 API. Legacy `v1`, `v2` and `v3` paths and response formats are outside this contract. `GET /profiles` returns the company-local business profile. Use its integer `id` for `GET /profiles/{id}` and `GET /profiles/{id}/balances?types=STANDARD`. The single standard USD balance reflects the company's first canonical USD bank account ordered by source ID. It is not an aggregate of all bank accounts. `types=SAVINGS` returns an empty list; balance creation and jars are unsupported. Create a USD recipient with `POST /accounts`: ```json { "profile": 10001, "currency": "USD", "type": "aba", "accountHolderName": "Example Supplier", "ownedByCustomer": false, "details": { "legalType": "BUSINESS", "abartn": "064000020", "accountNumber": "40000000000", "accountType": "CHECKING", "address": { "country": "US", "firstLine": "1 Example Way", "city": "New York", "state": "NY", "postCode": "10025" } } } ``` The recipient's `id` is an integer, and its name is returned in `name.fullName`. `GET /accounts` returns a `content` array and seek positions; use `seekPositionForNext` on the next request and stop when `content` is empty. Pages contain at most 20 recipients. Filters include profile, currency, active state and ownership, with `id` or `currency` ordering. `DELETE /accounts/{id}` deactivates the recipient; direct GET still returns it with `active: false`. Account details are validated structurally without a real bank lookup. Create a quote at `POST /profiles/{id}/quotes` with `sourceCurrency: "USD"`, `targetCurrency: "USD"` and exactly one positive `sourceAmount` or `targetAmount`. Amounts have at most two decimal places. Quotes use a synthetic rate of one, zero fees and a 30-minute lifetime. They do not represent live Wise pricing. Supply `targetAccount` when creating the quote or attach it with `PATCH /profiles/{id}/quotes/{quoteId}`. An expired or used quote cannot change. `POST /transfers` accepts `targetAccount`, `quoteUuid`, a UUID `customerTransactionId` and optional `details.reference`. The result remains `incoming_payment_waiting`. The quote changes from `PENDING` to `ACCEPTED` and can create only one transfer, including during concurrent requests. Reusing `customerTransactionId` for the same normalized request retrieves that transfer; changing the request returns a conflict. HTTP retries also require the original `Idempotency-Key`. Read transfers with `GET /transfers/{id}` or list them using profile, status, currency, creation-date, limit and offset filters. Cancel an unfunded transfer with `PUT /transfers/{id}/cancel` and an empty object. Its state becomes `cancelled`. These instructions never debit a bank account, reserve cash, create a payment allocation or post a journal entry. Funding, settlement, FX, other currencies, KYC, dynamic requirements, batches and webhooks are unsupported. The response fixture is extracted from the [published Wise OpenAPI](https://docs.wise.com/_bundle/api-reference/@latest/index.json?download). USD recipient field names follow Wise's [published account example](https://www.postman.com/transferwise/transferwise-s-public-workspace/request/u3rk4k2/create-usd-account). ### Salesforce, Pipedrive, Chargebee and Zendesk These providers expose native routes as well as export packs. Point an HTTP client or an SDK with a configurable endpoint at its Fictix native base URL. Keep the runtime key in a server environment. Browser clients use an origin-bound browser token, as described in the native browser access guide. | Provider ID | Example path after the native base | Mutation format | | --- | --- | --- | | `salesforce` | `/services/data/v66.0/query?q=SELECT%20Id,Name%20FROM%20Account` | JSON; create with POST, update with PATCH, delete with DELETE | | `pipedrive` | `/api/v2/persons` | JSON; create with POST, update with PATCH, delete with DELETE | | `chargebee` | `/api/v2/customers` | JSON or `application/x-www-form-urlencoded`; writes use POST | | `zendesk` | `/api/v2/tickets` | JSON wrapped in `ticket`, `user` or `organization`; create with POST, update with PUT | Chargebee also accepts `chargebee-idempotency-key`, the official SDK's header. If both idempotency headers are present they must agree. A replay returns `chargebee-idempotency-replayed: true`. Its Product Catalog 2 subscription workflow is: create a customer, item family, plan item and item price; POST `/api/v2/customers/{id}/subscription_for_items` with `subscription_items`; then retrieve, update, cancel or reactivate that subscription. Invoice collection, proration, refunds and scheduled billing are outside this contract. Salesforce supports scalar SOQL selection and comparisons, one-field ordering, `COUNT()`, `LIMIT`, and `OFFSET`. Composite requests support references such as `@{account.id}` and `allOrNone` rollback. Joins, general aggregates, tooling, Apex, provisioning and bulk APIs are outside this contract. Pipedrive supports core collection operations and name search with cursor pagination. Saved filters, merges and changelogs are outside this contract. Zendesk comments are appended through ticket creation or update; closed tickets reject edits. Email delivery, attachments, bulk jobs and automations are outside this contract. These are approximate synthetic APIs. Shared customer fields update the same canonical company records. CRM organization/contact views can originate from one canonical customer, and deleting a provider record hides that provider's record while preserving the shared entity. Spreadsheet export IDs retain their existing import contract and can differ from native API IDs. ### Configure accounting before an external bank payout Brex transfers to external vendors, Chase ACH/wire payments, and Mercury recipient payments need a company posting account. This is a prerequisite for the staging accounting workflow below; production payout accounting has not been qualified by these staging checks. An organization owner or administrator selects the account in **Settings → Company → Bank payout posting account** and chooses **Save account**. Reload the page and confirm the same account is displayed before sending a payment. If that control is absent in your environment, customer setup is unavailable there; do not work around it by copying a Console session token or using an operator credential. Choose an active canonical general-ledger account in the payout bank account's currency. Bank accounts, accounts receivable, accounts payable, and accounts linked to a bank are excluded. Saving the choice does not validate a future payment's currency; that check happens when the payment is posted. The setting is shared by these three providers within one company, so change it explicitly when the intended posting account changes. **Clear saved account** removes the choice and prevents subsequent external payouts until an account is saved again. These controls do not change the selected services, plan, or key scopes. A native caller still needs an entitled plan and a key with `write` scope, plus the route's idempotency key. A missing posting-account choice returns HTTP 400 with a message asking you to configure the counteraccount. The native error envelope depends on the provider: Brex includes code `CONFIGURATION_REQUIRED`, Chase reports `invalid_parameter` with context, and Mercury includes the message in `errors.general`. Fix the company setting before retrying; do not treat that response as a completed payment. Internal transfers between company bank accounts are a separate workflow. After a successful external payment, retrieve the provider operation and bank balance, then check canonical `bank_transactions`, `journal_entries`, and `gl_entries` or the exported books. The posting debits the chosen account and credits the bank's cash account. Readback must show one payment and one balanced posting, including after the provider's documented retry/replay behavior. ### Brex cash and cards Read cash accounts at `/v2/accounts/cash` and transactions at `/v2/transactions/cash/{account_id}`. Create virtual cards through `/v2/cards` with an owner from `/v2/users`. Card limits and lock, unlock and terminate operations are persistent and shared with execute. Create a vendor at `/v1/vendors` with ACH `payment_accounts`; use its returned `payment_instrument_id` in `/v1/transfers`. Transfers are processed immediately in the synthetic ledger and debit the selected account once. Amounts in the native API use integer cents; execute amounts use dollars. Physical card delivery, PANs, approval policies, international wires and external bank settlement are outside this contract. ### Mercury payments Use `/api/v1/accounts` and `/api/v1/recipients` for account and recipient discovery. Create recipients with `POST /api/v1/recipients`, using `name`, `emails` and ACH `electronicRoutingInfo` or `domesticWireRoutingInfo`, including their US address. Recipient creation requires an `Idempotency-Key` header. Do not put `idempotencyKey` in the recipient JSON; that body field is supported for Mercury payment and transfer requests. Send money with POST `/api/v1/account/{account_id}/transactions`; pass a positive dollar `amount`, `recipientId`, `paymentMethod` and a unique `idempotencyKey`. Repeating the body key returns HTTP 409 with the original transaction. If you also send an `Idempotency-Key` header, the two keys must agree. POST `/api/v1/transfer` moves funds between project accounts and returns the paired debit and credit transactions. Synthetic payments settle immediately; the balance and ledger update in the same transaction. Native and execute calls share recipients and money movements. Check delivery, international payments, approvals and external bank settlement are outside this contract. ### Gmail mailbox Use `/gmail/v1/users/me` for the project's mailbox. POST `/messages/send` with `{"raw":""}` to store a sent message. Drafts use `{"message":{"raw":"..."}}`; sending a draft removes it and creates a sent message in the same thread. Native calls and execute email operations share messages, labels, drafts and history. No email leaves the synthetic mailbox. Message and thread lists support `maxResults`, `pageToken`, repeated `labelIds`, and basic search terms such as `from:`, `subject:`, `in:`, `has:attachment`, `after:` and `before:`. Terms are combined with AND; prefix a term with `-` to exclude it. History requires `startHistoryId` and accepts `historyTypes` and `labelId`. Raw MIME attachments retain their bytes. Pub/Sub watches, mailbox settings, media upload sessions and advanced search are outside this contract. ### Chase Embedded Payments The native contract targets JPMorgan Embedded Payments: accounts and recipients use `/embedded/v1/accounts` and `/embedded/v1/recipients`; transactions use `/embedded/v2/transactions`. Discover the synthetic `clientId` and account IDs from the account list. Read balances at `/accounts/{id}/balances`. Create a recipient with `partyDetails`, `type: "RECIPIENT"` and a US checking or savings `account` containing `routingInformation`. Supported rails are ACH and domestic WIRE. POST a transaction with a unique `transactionReferenceId`, `debtorAccountId`, `recipientId`, positive dollar `amount`, `currency: "USD"` and `type`. A TRANSFER instead uses `creditorAccountId`. A successful synthetic transaction is immediately COMPLETED and updates cash once. Retrying with the same HTTP idempotency key replays the response; reusing the reference in a new request returns 409. Account provisioning, RTP, international payments, verification and bank settlement are outside this contract. The response schema fixtures retain the published account/recipient 1.0.58 and transaction 2.0.58 contracts from [JPMorgan's documentation](https://developer.payments.jpmorgan.com/api/embedded-finance-solutions/embedded-payments/embedded-payments/recipients). ### Monday GraphQL POST `/v2` with `query`, optional `variables` and `operationName`. Queries need `read`; mutations need `write` and an `Idempotency-Key`. The selected GraphQL operation determines the required scope. Variables, aliases, fragments and directives are supported. Mutation fields commit together; any field failure rolls back all writes in that request. Read `boards`, `users`, `items` and `items_page`; continue a page with `next_items_page`. Create items and groups, change column values, append/delete updates, archive/delete items, or move them between groups on their board. Generated boards expose status, due date, one assignee and priority text columns. Read `columns.settings_str` for status labels and numeric indices. Column writes accept JSON-encoded strings. Filters accept string or numeric `CompareValue` variables, with equality, text containment, empty checks and ordering. Queries are limited to 64 KiB, 12 selection levels and 1,000 expanded selections. Page size is at most 500. Board creation, custom columns, teams, subitems, subscriptions and webhooks are outside this contract. See the upstream [item pagination contract](https://developer.monday.com/api-reference/reference/items-page). ### Sage Intacct XML POST XML to `/ia/xml/xmlgw.phtml`. Use the usual Intacct `request`, `control`, `operation` and `function` envelope with `dtdversion` 3.0. The Fictix runtime key authenticates the request. Vendor passwords and session values are discarded; do not provide real Intacct credentials. Reads need `read`; any write function needs `write`. Writes require an `Idempotency-Key`, or `true` with a stable control ID. A retry replays identical XML bytes. Read CUSTOMER, VENDOR, GLACCOUNT, APBILL, ARINVOICE, GLBATCH, CHECKINGACCOUNT and SAVINGSACCOUNT through `read`, `readByName`, `readByQuery`, `query` and `readMore`. Create/update/delete is supported for customers, vendors and GL accounts. `getAPISession` returns a synthetic session. XML queries support explicit fields, scalar comparisons, boolean filters and ordering. Legacy `readByQuery` accepts one comparison; use `query` for compound filters. Return format is XML. Requests are limited to 1 MiB and 50 functions; responses to 4 MiB and 10,000 records. DTD and entity declarations are rejected. With `transaction="true"`, any failed function aborts the entire operation. With `false`, successful functions persist independently. Inspect each XML result's status, including on HTTP 200. The execute API additionally creates/posts bills, creates invoices, allocates payments, makes deposits/transfers and creates/posts balanced journals. These operations use the same canonical documents and cash accounts as native reads. Payment allocations reject overpayment; cash movements reject insufficient funds; journal posting updates GL balances once. Bill/invoice posting does not automatically produce GL journal entries. Historical balance snapshots, tax calculation, multi-currency accounting and external settlement are outside this contract. The optional official JavaScript SDK test covers the XML customer lifecycle; SDKs that enforce an Intacct hostname need a custom HTTP transport. See the upstream [XML gateway reference](https://developer.intacct.com/web-services/). Execute aging uses current outstanding balances and due-date buckets. The trial balance uses current GL balances. The cash-flow response reports bank ledger inflows/outflows for the requested dates; it does not classify investing and financing activity or claim a full accounting cash-flow statement. ### Tasks, envelopes and files ClickUp starts at `/api/v2/team`, followed by `/team/{id}/space` and `/space/{id}/list`. Create tasks with POST `/api/v2/list/{id}/task`; update with PUT `/api/v2/task/{id}`. Dates use Unix milliseconds. Updates to assignees use `{"assignees":{"add":[123],"rem":[]}}`; creation takes an array of IDs. Status, assignee and tag filters accept repeated `statuses[]`, `assignees[]` and `tags[]` parameters. List pages contain at most 100 tasks. DocuSign account discovery is GET `/oauth/userinfo`. Use the returned account ID under `/restapi/v2.1/accounts/{accountId}` for envelopes and templates. POST `/envelopes` accepts `documents`, `recipients` and `emailSubject`, or a `templateId` and `templateRoles`. PUT `/envelopes/{id}` supports `sent` and `voided` transitions. Documents download as bytes. Signing ceremonies and Connect webhooks are outside this contract. Dropbox uses POST for its native file RPCs. `/2/files/upload` takes raw bytes and a `Dropbox-API-Arg` JSON header containing `path` and `mode`. `/2/files/download` takes the same header with a path or file ID, and returns bytes plus a `Dropbox-API-Result` metadata header. Metadata, download and folder-list RPCs need only the `read` scope. Both Dropbox API and content host paths use this one Fictix native base. OneDrive uses Graph paths such as `/v1.0/me/drive/root/children` and `/v1.0/me/drive/root:/example.txt:/content`. PUT content takes raw bytes; GET content returns those exact bytes. Folder creation uses POST children with `name` and `folder: {}`. PATCH an item to rename or move it; DELETE removes it and its descendants. `If-Match` accepts the item's current `eTag`. The file APIs preserve arbitrary bytes, including JSON and empty files. Uploads are limited to 1 MiB per request. Generated finance files are seeded independently for each storage provider. Upload sessions, sharing, delta synchronization and subscriptions are outside this contract. ## Freshdesk support workflows Use `/native/freshdesk/api/v2` beneath the company route. Contact and company CRUD, agent discovery, ticket CRUD, ticket replies and private/public notes persist in the generated company. Creating a ticket with an email can create its requester. Read and edit conversations through their ticket, and resolve tickets using status `4` (`5` closes them). Lists support `page`, `per_page` and a next-page `Link`; ticket lists default to the past 30 days. Use `include=description,requester` on lists and `include=conversations` on a ticket detail to inspect its first ten conversations. Supply a Fictix runtime key and `Idempotency-Key` for writes. Reusing a key with the same request preserves the original result. Replies and notes remain synthetic. Attachments, configured custom fields, automation, structured chat bodies and analytics are outside this adapter's contract. ## Front discussion and task workflows Use `/native/front` beneath the company route. Contacts support CRUD and handle changes, with email/phone handles linked to canonical customer fields. Read `inboxes` and `teammates`, then create a `discussion` with its initial `comment`, or a `task` with a description and optional future `due_at` timestamp. Both require an inbox or a list of teammates. Comments, assignment, archival, trash and deletion persist. A conversation must be in the trash before permanent deletion; only a comment's creating Fictix credential may edit its body. Collections use Front's `_results` response and `_pagination.next` links. Contact timestamp filters and conversation status filters use the JSON `q` parameter. Write calls require `Idempotency-Key`, including DELETE operations that carry a JSON body. Mail channel sending/import, attachments, contact lists, custom field definitions, ticketing status categories and automations are outside this contract. ## Help Scout inbox workflows Use `/native/help_scout/v2` beneath the company route. Create a customer, read its `Resource-ID` response header, and retrieve it using the `Location` header. Creates have empty response bodies. The same headers survive idempotent replay. Customer PATCH requests take an array of operations; PUT overwrites profile fields, clearing omitted fields while retaining email and phone entries. Read `mailboxes` and `users`, then create a conversation with a customer and at least one thread. Customer messages, replies, notes, chat and phone threads, assignment, status updates and draft publication persist. Conversation PATCH takes one operation object. Published replies reactivate a conversation unless they specify another status; imported customer threads preserve its status. Lists use HAL `_embedded`, `_links` and `page` objects; conversation lists default to active conversations. Use `status=all` to include closed ones. Writes require `Idempotency-Key`. Customer deletion removes that provider's customer and associated conversations from its API. Advanced search, configured custom fields, teams, organizations, attachments, automated audit threads, merging, reports and webhooks are outside this contract. The inbox and user directory are synthetic, and no live messages are sent. ## Intercom customer and conversation workflows Use `/native/intercom` beneath the company route with `Intercom-Version: 2.16`. Contacts, company upserts and contact/company associations share canonical customer records. Create a conversation from a contact, then add replies or notes, assign an admin, close, reopen or snooze it. Creates return HTTP 200; conversation creation returns a message with its `conversation_id`. `POST contacts/search`, `POST conversations/search` and `POST companies/list` are reads and accept read-only keys without an idempotency header. Search cursors bind to the company and query. Contact timestamp searches compare UTC calendar days; the stored timestamps retain seconds. Writes require `Idempotency-Key`. Custom attributes, teams, brands, attachments, quick replies, contact archive/merge, Messenger, automation, reports and webhooks are excluded. For server-side code using `intercom-client@7.0.3`, its public configuration supports the native base URL and headers: ```typescript const client = new IntercomClient({ token: "fictix", baseUrl: `${companyBaseUrl}/native/intercom`, headers: { Authorization: "", "X-API-Key": process.env.FICTIX_RUNTIME_KEY, "Intercom-Version": "2.16", }, }); const contact = await client.contacts.create( { role: "user", email: "customer@example.test", name: "Test customer" }, { headers: { "Idempotency-Key": crypto.randomUUID() } }, ); ``` The token placeholder satisfies the SDK constructor; the runtime key authenticates the request. The additional header selects 2.16 because this SDK release's typed version option ends at 2.14. All conversations remain synthetic. ## Close CRM workflows Use `/native/close/api/v1` beneath the company route. Leads, contacts, opportunities, tasks and Note activities support creation, independent reads, updates with `PUT`, and deletion. A lead can include nested contacts when created; a contact or opportunity without a lead creates one atomically. Invalid nested records roll back the entire write. Lead deletion removes its Close children while retaining the underlying shared business records. Opportunity values use integer minor units. Monthly values are multiplied by 12 for annualized totals; list aggregates cover all matching records before pagination. The synthetic workspace provides one Sales pipeline, Active/Won/Lost opportunity statuses, and Potential/Qualified/Bad Fit lead statuses. A transition to Won supplies `date_won` when missing and respects `X-TZ-Offset`. Reopening a won opportunity preserves its existing win date. Tasks support lead and outgoing-call records, assignment and completion; these records do not place calls. Lists use `_limit` (1–100), `_skip` (0–10000), `data` and `has_more`. Contacts can be filtered by lead. Opportunities, tasks and notes support the documented simple relationship and date filters implemented in the connector; unsupported query parameters return a validation error. `_fields` accepts top-level field names. Status, pipeline and user discovery are read-only. The official `closeio==2.1` Python package can use its public `API` class with a custom base URL and the Fictix runtime header: ```python from closeio_api import API client = API(company_native_base + "/api/v1/", api_key="fictix", tz_offset="0") client.session.headers["X-API-Key"] = runtime_key lead = client.post("lead", {"name": "Example"}, headers={"Idempotency-Key": operation_id}) ``` This covers plaintext notes, core CRM records and task workflows. Rich-text HTML input, attachments, configured custom fields, advanced/text search, grouped reports, merge, moving contacts with linked records, automation, configuration writes and webhooks are outside this contract. Browser callers use origin-bound short-lived bearer tokens. No live Close requests or communications are sent. ## GitHub issue workflows Use `/native/github` beneath the company route. `GET user/repos` discovers the synthetic private repository `fictix/demo-workspace`. Issues share the company's task records and support creation, edits, assignment, labels, milestones, comments, closing and reopening. Label and milestone configuration supports creation, editing and deletion. Issue numbers remain stable across task edits. The default REST API version is `2022-11-28`. Set `X-GitHub-Api-Version` to `2026-03-10` for the newer contract; it accepts `assignees` and rejects the removed singular `assignee` write field. Responses identify the selected version. Lists support page/per-page parameters and scoped `Link` URLs. JSON GET responses support `ETag` and `If-None-Match`. Writes require `Idempotency-Key`. The official Octokit client can use a custom base URL and public request hook: ```typescript const client = new Octokit({ baseUrl: `${companyBaseUrl}/native/github` }); client.hook.before("request", (options) => { options.headers["X-API-Key"] = process.env.FICTIX_RUNTIME_KEY; options.headers["X-GitHub-Api-Version"] = "2026-03-10"; }); const issue = await client.rest.issues.create({ owner: "fictix", repo: "demo-workspace", title: "Example task", headers: { "Idempotency-Key": crypto.randomUUID() }, }); ``` Repository and collaborator discovery are read-only. This contract covers issue tracking and Markdown bodies. Git transport, repository content, branches, commits, pull requests, reviews, Actions, GraphQL, rendered HTML, notifications and webhooks are excluded. Repository URLs describe synthetic metadata; these workflows do not create repositories or issues on github.com. ## Linear issue and project workflows Send GraphQL JSON bodies to `/native/linear/graphql` beneath the company route. Read `teams`, `users` and `workflowStates` to discover the synthetic Engineering team, generated employees and Backlog/Todo/In Progress/Done/Canceled states. Issue mutations share canonical tasks; project renames update the names on their tasks. Markdown comments, labels, assignment, parent issues and project membership persist and can be read through their related records. Queries accept read-only runtime keys without `Idempotency-Key`. Mutations require write scope and an idempotency key. Reuse that key for a retry; choose a new key for each different mutation. All mutation fields in one request commit together or roll back together. Aliases, fragments, variables and named operation selection use GraphQL schema execution. The official `@linear/sdk@93.0.1` uses public endpoint and header options: ```typescript const client = new LinearClient({ apiKey: "fictix", apiUrl: `${companyBaseUrl}/native/linear/graphql`, headers: { "X-API-Key": process.env.FICTIX_RUNTIME_KEY }, }); const teams = await client.teams(); client.client.setHeader("Idempotency-Key", crypto.randomUUID()); const created = await client.createIssue({ title: "Example task", teamId: teams.nodes[0].id, }); const issue = await created.issue; ``` Lists use Relay `nodes`, `edges` and `pageInfo`, with `first`/`after` or `last`/`before`. Page sizes are 1–100. Cursors bind to the company, generation, relationship and filters. Basic field comparisons, related-record filters and bounded `and`/`or` groups are supported. Use absolute timestamps and `orderBy` with `createdAt` or `updatedAt`; custom sort expressions and relative durations are excluded. Archived and trashed issues/projects are hidden from default lists; `includeArchived: true` includes them. Delete trashes these records and unarchive restores them. Explicit permanent issue deletion removes its Linear projection. The schema is a supported subset of Linear's published contract. Team, user and status configuration, rich document state, cycles, roadmaps, initiatives, releases, external integrations, reactions, attachments, subscriptions, notifications, webhooks and automation are excluded. These workflows remain inside the generated company and do not send requests or notifications to Linear. ## Execute routes Chase, Monday, Sage Intacct, ClickUp, DocuSign, Dropbox and OneDrive expose both execute and native APIs backed by the same project records. FCI provides derived financial summaries through execute and retains preview status. ```http POST /api/v1/orgs/{company_slug}/execute X-API-Key: YOUR_RUNTIME_KEY Content-Type: application/json { "org_id": "YOUR_COMPANY_PROJECT_ID", "user_id": "your-integration", "turn_id": "unique-operation-id", "capability_key": "sage_intacct.customers.list", "args": {} } ``` The legacy `org_id` field identifies the generated **company project**, not the workspace organization. Check the response's `status`: execute errors may use HTTP 200 with `status: "error"`. Reuse `turn_id` when retrying an operation. | Provider | Workflow covered by generated-company verification | | --- | --- | | Chase | Read accounts; create a synthetic ACH/wire payment; retrieve it and its changed balance | | ClickUp | Read generated teams/lists; create, update and retrieve tasks and comments | | DocuSign | Create a synthetic envelope; retrieve documents and recipients; send and void it; create from a template | | Dropbox, OneDrive | Create folders; upload, list and download the same binary content | | FCI | Read financial summaries derived from the generated company | | Monday | Create and update items; preserve board/group membership and comments | | Sage Intacct | Manage customers/vendors and GL accounts; create and post bills/journals; allocate payments and move cash | File content uses base64. File paths are scoped to the company and provider. Missing files, tasks, envelopes and payments return an error. Synthetic DocuSign state changes do not send an email, and synthetic Chase payments do not move money through a bank. Future-dated Chase execution is unsupported. ## Export packs Download a ZIP from `/api/v1/orgs/{company_slug}/export-pack/{target}`. The seven targets are `qbo`, `hubspot`, `asana`, `chargebee`, `pipedrive`, `salesforce` and `zendesk`. These contain generated company records in the documented spreadsheet layouts. Some captured pack members intentionally contain headers only; consult each pack's schema for supported artifacts. ## Coverage and fidelity The workflows here describe supported operations, request forms, and observable state changes. Additional vendor endpoints need their own compatibility verification. Inspect the [capability metadata](/api-reference/capability-matrix) and test the exact methods your software uses. Native-shaped responses remain approximate; they are not a certification of full upstream API parity. ### Paddle Billing Use provider ID `paddle` with `Paddle-Version: 1` (also the default). Manage customers, their addresses/businesses, products and prices through Paddle's usual POST, GET and PATCH paths. Archive catalog records with `status: archived`; list them explicitly with `status=archived`. Lists use `per_page`, `after` and `meta.pagination.next`, scoped to the company. For a manual invoice, create a product and price, then POST `/transactions` with `collection_mode: manual`, `items: [{price_id, quantity}]` and `billing_details.payment_terms: {interval: day, frequency: 30}`. Add the `customer_id` and a matching `address_id` to make it ready, then PATCH its `status` to `billed`. Billing creates an invoice visible through the canonical accounting records. Recurring prices also create a subscription; retrieve its ID from the billed transaction. There is no POST `/subscriptions` endpoint. Subscriptions support immediate pause/cancel with `effective_from: immediately`. Resume before the previous billing period ends with `on_resume: continue_existing_billing_period`. Scheduled changes, new renewal invoices and proration are excluded. Subscription customer/address/business, billing details and custom data can be updated. Billed transactions permit custom-data edits only; draft/ready transactions may be canceled. Catalog prices support USD, EUR and GBP. Manual financial writes support USD, zero-tax fixtures without discounts, and positive totals up to `999999999999` cents. Billing does not collect money or post a general-ledger journal. Automatic payments, checkout, hosted portals, tax calculation, adjustments/refunds, trials, reports and webhooks are outside this contract. Portal URLs are synthetic metadata, not functioning hosted pages. The official JavaScript `@paddle/paddle-node-sdk@3.10.0` is tested without changing its serialization or response classes. Its `environment` option accepts the Fictix base URL at runtime and `customHeaders` carries the runtime key and idempotency key. The package's TypeScript `Environment` enum does not advertise arbitrary base URLs. No live Paddle calls, payments or notifications are sent. ### Recurly Use provider ID `recurly` and `Accept: application/vnd.recurly.v2021-02-25`. Create accounts with a unique `code` and fixed-price plans with `code`, `name` and `currencies: [{currency: USD, unit_amount: 25}]`. Account codes and plan codes can be fetched with `/accounts/code-{code}` and `/plans/code-{code}`. Accounts map to shared customers; plans map to shared inventory items. Plan updates use PUT and return 201, following the published API specification. POST `/subscriptions` with `plan_code`, `account: {code}`, `currency: USD`, `collection_method: manual` and an optional quantity. This creates the account if needed, the subscription and one canonical invoice in the same transaction. Retrieve invoices through `/subscriptions/{id}/invoices`, `/accounts/{id}/invoices`, `/invoices/{id}` or `/invoices/number-{number}`. Subscription UUID lookup uses `/subscriptions/uuid-{uuid}`. Billing lists contain records created through this Recurly adapter. Manual net terms include the additional 24 hours used by Recurly: `net_terms: 30` becomes due 31 days after invoice creation. Subscription metadata changes affect future settings; they do not rewrite an existing invoice. PUT `/subscriptions/{id}/cancel` with `timeframe: term_end` or `bill_date` to mark the subscription canceled. Reactivate it before its expiry with PUT `/subscriptions/{id}/reactivate`. DELETE `/subscriptions/{id}?refund=none` terminates it immediately and leaves the invoice outstanding. Account deactivation requires all its Recurly subscriptions to be terminated; PUT `/accounts/{id}/reactivate` restores the account. Removed plans remain readable and cannot be used for new subscriptions. Lists use `limit` (1–200), timestamp `sort`, `order`, `begin_time`, `end_time` and scoped cursor URLs in `next`. Account lists support email/subscriber filters; plans, subscriptions and invoices support their documented state filters. The `ids` filter returns at most 200 IDs and cannot be combined with pagination or other filters. Unsupported parameters return validation errors. Plans support USD/EUR/GBP, fixed prices with at most two decimal places, day/month intervals, no setup fee and no trial. Financial writes support USD totals from 0.01 to 9,999,999,999.99 with zero tax. Renewal flags and term dates are stored; automatic renewals and scheduled expiry do not execute. Proration, pauses, payment collection/refunds, purchases, coupons, add-ons, tax calculation, general-ledger posting, webhooks and HEAD list counts are outside this contract. The official `recurly@4.79.0` Node SDK is tested with its serializers, resource classes, errors and pagination unchanged. It has no public base URL option, so the test routes its HTTPS transport to a loopback bridge. Public request headers carry `X-API-Key` and `Idempotency-Key`; applications using this SDK need a transport override or proxy for the Fictix endpoint. No live Recurly traffic or payment collection occurs. ### PayPal Use provider ID `paypal`. Create catalog products at `/v1/catalogs/products` with `name` and `type` (`PHYSICAL`, `DIGITAL` or `SERVICE`). Products map to shared inventory items. Their published PATCH operations support add, replace and remove for description, category, image URL and home URL. Product name and type are immutable through PATCH. Custom product IDs must be 6–50 URL-safe letters, digits or `._~-`; omitted IDs receive the `PROD-` prefix. Product lists use page numbers, `page_size` up to 20 and optional `total_required=true`. Search generated payment history through `/v1/reporting/transactions` with absolute `start_date` and `end_date` timestamps spanning at most 31 days. Page sizes can reach 500. Supported filters are exact transaction ID, T-code, status and currency. Select `fields=transaction_info`, `payer_info` or `all`. Captured-payment records come from positive canonical customer payments; retrieve a report's transaction ID at `/v2/payments/captures/{id}`. POST `/v2/payments/captures/{id}/refund` with an empty object to request the remaining captured amount, or `amount: {currency_code: USD, value: "0.50"}` for a partial refund. Read it at `/v2/payments/refunds/{id}`. Requests remain `PENDING` and reserve that amount against further requests. Concurrent requests cannot reserve more than the capture. Pending refunds appear in transaction searches with `balance_affecting_records_only=N`, status `P` and event `T1107`. They do not change the original payment, invoice, cash or general ledger. Writes accept `PayPal-Request-Id` (1–38 characters) or Fictix `Idempotency-Key`; both headers must agree when supplied together. Responses use full representation, including with `Prefer: return=representation`. Minimal representation, refund settlement, checkout/orders, authorizations, vault, billing plans/subscriptions, payouts, balance reports, disputes, mock-response headers, delegated merchant assertions and webhooks are outside this contract. The official `@paypal/paypal-server-sdk@2.5.0` test uses configured Axios headers to carry `X-API-Key` and its `unstable_httpClientOptions` adapter hook to route requests into the local bridge. A local OAuth token prevents vendor token fetches; Fictix authenticates the runtime key. Generated request and response schemas, headers and error handling remain unchanged. This SDK does not expose a general base URL option; applications need a transport override or proxy for Fictix. No PayPal traffic or real refund is sent. ## Adyen Checkout workflows Use `/api/v1/orgs/{slug}/native/adyen/v72` with your Fictix runtime key in `X-API-Key` and `merchantAccount: "FictixDemo"`. Each company has its own merchant data. Writes require `idempotency-key` of at most 64 characters; replays return the original response and conflicting bodies return error 704. `POST /paymentMethods` is a read operation and works with a read-only key. Checkout v72 supports Visa and Mastercard method discovery, allow/block lists, shopper token creation/listing/deletion, manual authorization, and asynchronous capture, cancellation and refund requests. Tokens are scoped to their shopper reference, support the three published recurring processing models, and can also be stored during authorization. Repeated token creation for the same shopper, card and expiry returns the existing token. Deleting a token prevents reuse. For test-card input, use `paymentMethod.type: "scheme"`, `encryptedCardNumber: "test_4111111111111111"` for Visa or `"test_5555555555554444"` for Mastercard, `encryptedExpiryMonth: "test_03"`, `encryptedExpiryYear: "test_2030"`, and `encryptedSecurityCode: "test_737"`. Valid future month/year combinations are accepted. These are Adyen's documented synthetic test credentials; the adapter does not decrypt cards. Authorization requires a positive integer USD amount in cents, a reference of at most 80 characters, an absolute HTTP(S) return URL, and `additionalData: {"manualCapture": "true"}`. It returns `Authorised` and keeps the payment separate from settled customer payments. Captures and cancellations apply to these open native authorizations. Refunds apply to existing posted or settled canonical customer payments. Modification responses return `received`; amounts are reserved so competing requests cannot over-capture or over-refund. A pending capture is not eligible for a refund. Pending cancellation prevents further capture. Cash balances, invoice balances and journal entries remain unchanged until settlement, which this workflow does not implement. The official `@adyen/api-library` 32.1.0 SDK is exercised against these routes, including its generated serializers, authentication, token methods, payment methods, modification responses and parsed errors. The SDK has no public Checkout base-URL setting; route its HTTP transport to the company native prefix while retaining the `/v72/...` path. The checked-in SDK test redirects only the socket destination to a local HTTP bridge and prohibits upstream traffic. Automatic/delayed capture, authorization expiry, refund settlement, declines, 3DS, sessions, hosted checkout and payment links, other payment methods, splits, balance-platform operations and webhooks are outside this contract. ## QuickBooks query and amount behavior The QBO query route supports `SELECT * FROM `, optional single equality filters, `STARTPOSITION`, and `MAXRESULTS` (1–1000). All entities support `Id`; invoices support `DocNumber`/`CustomerRef`, bills and bill payments `VendorRef`, payments `CustomerRef`, customers and vendors `DisplayName`, accounts and items `Name`, and journal entries `DocNumber`. Filters use exact string values and backslash escaping for apostrophes. Filtering happens before pagination. Unsupported operators, compound predicates, sorting, and extra statements return a QBO fault. Invoice and bill totals use the shared accounting total; money fields are JSON numbers, including zero balances. ## FreshBooks accounting workflows Use `/api/v1/orgs/{slug}/native/freshbooks` with `X-API-Key`, `Api-Version: alpha` (the default), and `Idempotency-Key` on writes. Identity discovery at `GET /auth/api/v1/users/me` returns the company-local `FICTIX` account. Accounting routes start with `/accounting/account/FICTIX`: `/users/clients`, `/items/items`, `/invoices/invoices`, and `/payments/payments`. Clients, items and invoices support POST, list/single GET, and PUT by ID. Payment allocations support list/single GET. Bodies use the singular resource wrapper, such as `{"client":{"fname":"Ada","lname":"Lovelace"}}`. Create an invoice with an active `customerid`, `create_date` in `YYYY-MM-DD` format, and ordinary `lines` containing `name`, decimal `qty`, and `unit_cost: {"amount":"12.50","code":"USD"}`. Up to 100 lines are accepted; each line is rounded to cents before totals are summed. Omitted invoice numbers are allocated uniquely within the company. `due_offset_days` supports 0–3650. PUT replaces the complete line array when supplied; omitted fields are retained. Invoices start as drafts outside the shared invoice collection. PUT `{"invoice":{"action_mark_as_sent":true}}` issues the invoice with the same native ID and exposes it to other integrations. This action sends no email. Only invoices created through this adapter can be updated; paid and partially paid invoices cannot be changed. Invoice and item price writes support USD. Client currency preferences also support EUR and GBP. Payment reads expose one row per existing invoice allocation, preserving its amount and invoice/customer links; unapplied balances are omitted. Lists support `page`, `per_page`, documented scalar search fields, and supported sort fields. Page zero becomes page one; page size defaults to 15 and is capped at 100. PUT `vis_state: 1` deletes a resource from normal reads, `2` archives it, and `0` restores it. The delete response contains the updated resource, matching the official SDK fixtures. Unsupported parameters return an error. The official `@freshbooks/api` 4.1.0 SDK is exercised through its public `apiUrl` setting and an Axios transport adapter that adds the Fictix runtime key and idempotency headers. Applications using this SDK need those transport headers; its OAuth bearer token alone does not authenticate with Fictix. SDK request and response conversion, money/date parsing, pagination and errors are preserved. Tax and discount calculation, automatic billing, payment collection, journal posting, invoice email, credits, expenses, projects, time tracking, reports, attachments, secondary contacts and webhooks are outside this workflow. ## Zoho Books accounting workflows Use `/api/v1/orgs/{slug}/native/zoho_books/books/v3` with your Fictix runtime key in `X-API-Key` and `Idempotency-Key` on writes. `GET /organizations` returns the company-local organization ID `100000000000001` and USD currency ID `100000000000002`. Every subsequent request requires `?organization_id=100000000000001`; another organization ID is rejected. `/contacts` supports customers and vendors, addresses and up to ten contact people with one primary contact. POST requires `contact_name`; PUT by contact ID requires `contact_name` and `contact_type`. The contact type cannot change. `/items` supports sales items with a name and numeric USD rate. POST and PUT require `name` and `rate`; a zero rate is valid. Contacts and items support GET by ID, list GET, and POST `/{id}/active` or `/{id}/inactive`. POST `/invoices` creates a draft with an active `customer_id` and `line_items` that reference active `item_id` values. Lines accept positive decimal quantity and an optional numeric rate; amounts are rounded to cents per line. Invoice totals must be positive. PUT by invoice ID replaces the complete line array and requires the customer ID again. Omitted invoice numbers are allocated uniquely. Dates use `YYYY-MM-DD`; payment terms support up to 3650 days. Invoice and item price writes use USD, without tax, discounts, shipping, adjustments or FX. POST `/invoices/{id}/status/sent` issues the draft into the shared invoice collection with the same native ID. The action sends no email. Issued invoices are immutable through this adapter. DELETE supports only drafts created through this adapter. Native-created contacts and items can be deleted when no financial transaction references them. A deleted draft releases those references. `/customerpayments` supports list and single reads of settled payments and their invoice allocations. Pending payments are omitted. Lists support documented scalar filters, supported status filters, `sort_column`, and stable ascending order. `page` starts at one; `per_page` defaults to 10 and accepts 1–200. Unsupported parameters return an error. JSON response contracts are checked against the published OpenAPI modules; invoice totals and balances follow the numeric wire examples where the schema's string annotation disagrees. Payment collection, journal posting, invoice email, recurring invoices, estimates, sales orders, purchasing, inventory movements, credit notes, vendor payments, reports, attachments, custom fields, portals, regional tax settings and webhooks are outside this contract. The workflow makes no live Zoho requests. ## Klaviyo profiles, lists and events Use `/api/v1/orgs/{slug}/native/klaviyo` as the SDK base URL, with a Fictix API key and `revision: 2026-07-15`. Writes require `Idempotency-Key`. JSON:API responses use `application/vnd.api+json`. These workflows are checked against Klaviyo's published [OpenAPI category specifications](https://github.com/klaviyo/openapi) and the official `klaviyo-api` Node SDK 23.0.0. - Profiles: list/read, create, update and synchronous `POST /api/profile-import` upsert. Email, phone and external ID resolve existing profiles; conflicting identifiers return 409. Names, email and phone remain linked to canonical customers. Custom properties and location fields merge; null clears submitted fields. At least one identifier must remain. - Lists: create/read/update/delete, read member profiles or their relationship IDs, and add/remove up to 1,000 profiles atomically. Repeated additions preserve the original join time. Deleting a list preserves its profiles. `additional-fields[list]=profile_count` reports current membership. Membership changes do not grant marketing consent. - Events: `POST /api/events` persists a submitted event and its profile/metric relationships, then returns an empty 202. Accepted synthetic events are immediately available through event list/read endpoints. The first event with a given profile, metric and `unique_id` is retained; when omitted, the event time to the second is the deduplication key. Optional `value` is represented as `$value` in event properties; it has no financial effect. - Metrics: list/read API metrics created by submitted events. No engagement, purchase or campaign metrics are fabricated from unrelated financial records. Profile and list reads support their published basic identifier/date filters. Event reads support profile ID, metric ID, datetime and timestamp comparisons. List-member reads support email, phone and join-date filters; metrics support integration name/category equality. Supported predicates compose with `and`, `or`, `not` or comma-separated conjunctions, bounded to 16 expressions and four nested levels. String literals use quotes; `any` accepts a string array. Unsupported fields/operators fail explicitly. Sorting is limited to the published fields implemented for each endpoint. Pagination honors profile/member limits of 1–100, list limits of 1–10, event limits of 1–1,000 and a fixed 200-metric page. Cursors bind to the company, incarnation, endpoint, query and ordered record IDs; insertions, removals or reordering require starting again. Next/previous links use the same Fictix native base URL. Unavailable links are omitted. Sparse fieldsets support implemented attributes and nested location fields. Event reads can include `profile,metric`; individual profile reads can include `lists`. Campaign delivery, email/SMS subscriptions, consent administration, SMS provisioning, attribution, predictive analytics, segments, flows, bulk jobs, profile merge/deletion, profile meta patches and `value_currency` are outside this workflow. Unsupported versions, requests and query parameters return errors. E.164 phone syntax is validated; upstream account-specific SMS rules are not simulated. No messages are delivered and no money or ledger entries change. ## Mailchimp audiences, members and tags Use `/api/v1/orgs/{slug}/native/mailchimp/3.0` as the Marketing SDK base URL, with a Fictix API key. Writes require `Idempotency-Key`. These workflows are checked against Mailchimp's published [Marketing API specification](https://github.com/mailchimp/mailchimp-client-lib-codegen/blob/main/spec/marketing.json) 3.0.91 and the official `@mailchimp/mailchimp_marketing` Node SDK 3.0.80. - Audiences: list/read, create, update and delete through `/lists`. Creation requires the published audience settings, contact and campaign defaults. Updates require the complete settings payload, including contact state and postal code. Deletion preserves canonical customers. - Members: list/read, create, update, `PUT` upsert and archive through `/lists/{list_id}/members`. The subscriber hash is MD5 of the lowercase email address. Email changes update that identifier. Names, email and phone remain linked to canonical customers. An archived member is excluded from ordinary reads; `status=archived` lists archived records, and upsert can restore them. - Tags: list a member's tags, add/remove named tags atomically, and search audience tags. Repeated additions preserve the original assignment time. - Merge fields: read the fixed `FNAME`, `LNAME` and `PHONE` definitions. These values update the corresponding canonical customer fields. The initial audience `f1c71ca001` derives its contacts from canonical customers with email addresses. Those initial contacts have `transactional` status; no marketing consent is inferred. Its sender and postal address are explicitly synthetic placeholders. Custom audiences contain only explicitly added members. Archiving preserves the canonical customer, and deleting the initial audience does not silently recreate it. Audience counts reflect current member statuses; campaign counts are zero because campaign activity is not simulated. Lists support email/date filters and creation-date sorting. Members support status, email type, unique email ID, VIP and timestamp filters, with sorting on the implemented opt-in, signup and change timestamps. Collection reads accept `count` from 1 to 1,000 and a nonnegative `offset`. `fields` and `exclude_fields` select response fields and cannot be combined. Unsupported query parameters and write fields fail explicitly. Errors use `application/problem+json`. Campaign creation or delivery, engagement reporting, automations, commerce, custom merge-field administration, interests, marketing permissions, notes, SMS, bulk jobs and permanent contact deletion are outside this contract. Submitted subscription statuses and campaign defaults are synthetic state; they do not deliver messages or change a live Mailchimp account. ## Deel personnel and organization structures Use `/api/v1/orgs/{slug}/native/deel/rest` as the base URL, with a Fictix API key. Writes require `Idempotency-Key`. These workflows use Deel's published [OpenAPI specification](https://api.letsdeel.com/openapi/rest/definitions) 1.25.0 and its operations dated 2026-01-01. `GET /people` and `GET /people/{hris_profile_id}` expose canonical personnel, stable UUIDs, names, work and personal emails, job titles, start dates, departments and manager/report relationships. Active and future start dates determine personnel status. Employment-contract arrays are empty because this workflow does not create Deel contracts. `GET /organizations` returns the current synthetic organization. People lists support name search, supported name/identifier/date/job/status sorts, repeated `hiring_types`, `hiring_statuses` and `person_statuses` query parameters, and repeated `fields` parameters for implemented top-level fields. Pages use `offset` and `limit`, defaulting to 100 records with a maximum of 200. The `page` object reports the filtered total and requested page size. Unsupported selectors, filters and relationship expansions return errors. `/hris/organization_structures` supports list/create/read/update/delete, with individual lookup, update and deletion also available through `/hris/organization_structures/external/{external_id}`. Structures hold named teams and optional external IDs. Creation uses `is_multiple_select`, its response uses `is_multi_select`, and later reads/updates use `is_multiselect`, following the published contracts. Updates require `teams`; existing team IDs preserve identity, and omitted teams are removed. Each structure supports up to 200 flat teams. PATCH and DELETE acknowledge success with an empty 200. The initial Departments structure reflects canonical department assignments. Renaming a department team updates the corresponding personnel records. Removing an assigned department or its structure fails atomically. Custom structures do not implicitly assign staff or modify payroll. Person creation, people without contracts, contract hiring/signing, invitations, payroll, benefits, payments, worker assignment, custom fields, roles, team hierarchies and webhooks are outside this workflow. No live Deel calls occur. ## Meta Ads account and campaigns Use `/native/meta_ads/v26.0` with a Fictix runtime key. Discover the synthetic account with `GET me/adaccounts`, or read `GET act_1000000001?fields=id,account_id,name,currency,timezone_name`. The account uses USD and UTC. Campaigns start empty. POST `act_1000000001/campaigns` with JSON or the official SDK's URL-encoded form: ```json { "name": "Website traffic test", "objective": "OUTCOME_TRAFFIC", "special_ad_categories": [], "daily_budget": 1000, "status": "PAUSED" } ``` The response supplies `id`. Read `GET {id}?fields=name,status,daily_budget` to verify persisted values. The daily budget is in currency minor units: `1000` represents USD 10.00 and is returned as the string `"1000"`. This workflow supports auction campaigns, `OUTCOME_TRAFFIC` or `OUTCOME_AWARENESS`, daily campaign budgets, `LOWEST_COST_WITHOUT_CAP` bidding, no special ad categories and no ad-set budget sharing. New campaigns can be `PAUSED` or `ACTIVE`. POST `{id}` to update supported campaign fields; objective changes are rejected. Updates return `{"success": true}` after persistence. Set `status` to `ARCHIVED` to archive a campaign, or use `DELETE {id}` to delete it. Archived and deleted campaigns cannot be edited; deletion remains available after archive. The runtime retains their status for inspection. Default campaign lists include `ACTIVE` and `PAUSED`; use the JSON-encoded `effective_status` query parameter to request `ARCHIVED` or `DELETED` records explicitly. `GET act_1000000001/campaigns` accepts comma-separated `fields`, `limit` up to 100, `before` or `after`, and `summary=true`. Cursors and next/previous URLs are bound to the company, incarnation, query and matching campaign state. Restart paging if that state changes. Writes need the `write` scope and use Fictix's shared idempotency ledger; field selection on writes is limited to `id` on creation. Read other fields with a separate GET. The official Python `facebook-business` 26.0.1 SDK can use the documented `FacebookSession.GRAPH` base URL and `requests` session attributes to target this runtime and send `X-API-Key`. No live vendor token is required. This bounded workflow does not create ad sets, ads, creatives or audiences, serve ads, spend money, produce insights, implement targeting, special categories, lifetime budgets, async/batch Graph requests or OAuth. ## Google Analytics 4 events and reports Use `/native/google_analytics_4` with a Fictix runtime key. The company's synthetic property is `properties/1000000001`, its web measurement ID is `G-FICTIX001`, and its reporting time zone is UTC. No web traffic is inferred from financial or personnel records: reports begin empty and use only events collected into that company's runtime. `GET v1beta/properties/1000000001/metadata` lists the supported dimensions and metrics. `POST v1beta/properties/1000000001:checkCompatibility` checks requested fields. Supported dimensions are `date`, `eventName`, `pagePath`, `pageLocation`, `hostName`, `pageTitle` and `platform`; metrics are `eventCount`, `totalUsers` and `screenPageViews`. User counts use distinct web client instances, including normalization of the documented GA client-cookie form. Send a web Measurement Protocol payload to `POST debug/mp/collect?measurement_id=G-FICTIX001` to validate without collecting: ```json { "client_id": "123456789.1234567890", "events": [{ "name": "page_view", "params": {"page_location": "https://sample.test/pricing", "page_title": "Pricing"} }] } ``` Valid requests return `{"validationMessages": []}`. To persist the same payload, use `POST mp/collect?measurement_id=G-FICTIX001`, which returns empty `204` only after the complete batch is saved. Batches accept up to 25 events with up to 25 scalar parameters each. Use a Fictix idempotency key to replay a write safely. Fictix authentication uses `X-API-Key`; the vendor `api_secret` query parameter is optional and is not used to authenticate this synthetic interface. POST `v1beta/properties/1000000001:runReport` to read the saved events: ```json { "dateRanges": [{"startDate": "7daysAgo", "endDate": "today"}], "dimensions": [{"name": "pagePath"}], "metrics": [{"name": "screenPageViews"}, {"name": "totalUsers"}], "orderBys": [{"metric": {"metricName": "screenPageViews"}, "desc": true}], "limit": "100" } ``` One date range is supported, with absolute dates or `NdaysAgo`, `today` and `yesterday`. Use `dimensionFilter` with exact, begins-with, ends-with, contains or in-list matches; bounded AND, OR and NOT groups are supported. Ordering uses requested columns, and `offset`/`limit` are int64 decimal strings. `rowCount` describes all matching groups before pagination. `batchRunReports` accepts up to five requests. All report, compatibility and debug-validation POSTs require only `read`; collection requires `write`. Counts are available immediately in this runtime, so a separate report can verify collection. This is a bounded web workflow. Invalid or unsupported collection fails with HTTP `400` and makes no changes; Google's upstream collector can instead return `2xx` and discard invalid events. Fictix always enforces the preceding 72-hour timestamp window, even with `RELAXED` validation. App streams, User-ID identity, geolocation, consent and advertising attribution, session/engagement/revenue metrics, custom definitions, realtime, pivots, cohorts, metric filters, quota simulation and OAuth are outside this scope. Metadata advertises only the implemented fields. The official `google-api-python-client` 2.200.0 REST discovery client is verified for metadata, reports, batch reports and compatibility using the published v1beta Discovery document and its public endpoint/HTTP transport options. ## Google Ads accounts, budgets and Search campaigns Use `/native/google_ads/v25` with a Fictix runtime key. Start with `GET customers:listAccessibleCustomers`; the generated company owns the synthetic account `customers/1000000001`, with USD currency, UTC time zone and `testAccount: true`. Campaigns and budgets start empty. POST `customers/1000000001/campaignBudgets:mutate` with an `operations` array. A budget `create` accepts `name`, a positive int64 decimal string `amountMicros`, and shared daily standard-delivery settings. Then POST `campaigns:mutate` under the same customer with a campaign `create` containing `name`, `campaignBudget`, `advertisingChannelType: "SEARCH"`, `manualCpc: {}`, an explicit `containsEuPoliticalAdvertising` enum and optional `status: "PAUSED"` or `"ENABLED"`. The response returns resource names; `responseContentType: "MUTABLE_RESOURCE"` also returns mutable fields. Each batch of up to 100 operations is atomic. `validateOnly: true` checks the batch without persisting it. POST `googleAds:search` or `googleAds:searchStream` with a GAQL `query`, for example: ```sql SELECT campaign.id, campaign.name, campaign.status, campaign_budget.amount_micros FROM campaign WHERE campaign.status != 'REMOVED' ORDER BY campaign.id ``` These POST endpoints need only the `read` scope. Supported resources are `customer`, `campaign` and `campaign_budget`, including related account/budget fields. Queries support explicit fields, one `=` or `!=` predicate, one ordering and a positive `LIMIT`. Search uses v25's fixed 10,000-row pages, opaque `nextPageToken`, and optional `searchSettings.returnTotalResultsCount` or `omitResults`. `pageSize` is rejected. Page tokens are bound to the company, incarnation, query, settings and matching state. SearchStream returns the published JSON array envelope in one response. Summary rows are unsupported. Update with an operation containing `update` and an exact camel-case `updateMask`. Budget updates accept `name` and `amountMicros`; campaign updates accept `name`, `status`, `campaignBudget` and `containsEuPoliticalAdvertising`. A `remove` operation marks a resource `REMOVED`; a budget cannot be removed while a non-removed campaign references it. Independently query the resource to verify every mutation, and use the same idempotency key for a retried write. This is a bounded REST interface. It does not claim Google Ads gRPC SDK support, serve ads, bill an account, fabricate spend or traffic, implement metrics, segments, ad groups/ads, temporary IDs, partial failures, manager delegation, OAuth, other campaign types or every upstream error detail. Unimplemented fields and operations return explicit errors. The official `google-api-python-client` 2.200.0 REST discovery client is verified against these routes using the published v25 document, its public `client_options.api_endpoint` override and a Fictix-authenticated HTTP transport. ## Justworks company and personnel Use `/native/justworks/v1` for company/member reads and job, department and manager updates. `GET /company` supplies the department IDs used in `PATCH /members/{member_id}`. The PATCH body accepts `job_title`, `department_id` and `manager_id`; success returns empty HTTP 204 after the canonical employee record is saved. References must exist in the same company. Self-management and reporting cycles return HTTP 422 without partial changes. Names, job titles, department and manager relationships remain consistent with the other HR providers. Existing department IDs survive moves out of a department. Member lists return `items` and an optional `next_cursor`. Paging defaults to 100 rows and accepts `limit` up to 100. Filters include `status=active` or `status=terminated`, plus `updated_at` and its `_gt`, `_gte`, `_lt`, `_lte` and `_ne` date variants. Cursors are bound to the company incarnation, query and roster state; restart paging after changing the roster. IDs are stable within the company. Absent source creation/update times use the synthetic `2000-01-01T00:00:00Z` baseline. The implementation is checked against the OpenAPI 1.32.5 document embedded in [Justworks' published API reference](https://public-api.justworks.com/v1/docs). It covers legal/preferred names, work emails, active status and known employment dates. It does not implement member creation or onboarding, payroll, paystubs, deductions, compensation, sensitive identity fields, benefits, offices, custom fields, OAuth flows or webhook delivery. Job and reporting-line updates have no payroll or payment effects. ## SAP business partners Use `/native/sap/sap/opu/odata/sap/API_BUSINESS_PARTNER` as the OData v2 base. `A_BusinessPartner`, `A_Customer` and `A_Supplier` support JSON list and single reads with SAP `d`/`results` envelopes. Generated customers and vendors have stable master-data IDs and matching customer or supplier references. The partner's `to_Customer` and `to_Supplier` navigation properties resolve those existing roles and can be expanded. Names reflect canonical records. Create a standalone organization with `POST A_BusinessPartner` and `{"BusinessPartnerCategory":"2","OrganizationBPName1":"Example business"}`. Category `1` creates a person and requires `LastName`; `FirstName` is optional. The implemented grouping is the synthetic `0001`. Creation returns HTTP 201, the assigned `BusinessPartner` and a `Location` header. Use `PATCH A_BusinessPartner('ID')` for name, search-term and language updates; success returns empty HTTP 204. An optional `If-Match` header checks the ETag returned by GET and rejects stale updates with HTTP 412. Fictix idempotency prevents duplicate creates. New partners have no customer or supplier role; these writes do not create financial transactions. Queries support `$top` up to 1000, `$skip`, `$inlinecount=allpages`, `$count`, supported-property `$select`, one string `eq` or `ne` predicate and one `$orderby` property. Default pages contain up to 100 records and include `__next` when more remain; explicit `$top` limits the total returned rows. Only JSON is supported. Unsupported query syntax fails explicitly. The workflow follows [SAP's published Business Partner metadata](https://github.com/SAP-samples/cloud-cap-samples-java/blob/main/srv/external/API_BUSINESS_PARTNER.edmx). SAP Cloud SDK clients can use public `setBasePath`, `addCustomHeaders` and `skipCsrfTokenFetching` options with a Fictix API key. SAP session cookies and CSRF token negotiation are unavailable. This subset does not implement deletion, deep inserts, role creation, addresses, tax or bank information, company codes, sales or purchasing areas, batch, metadata/XML, SOAP, payroll, invoices or other ERP APIs. Existing customer/vendor name updates remain visible through other native providers without changing their ledger records. ## Workday workers and check-ins Use `/native/workday/staffing/v7` as the base for the implemented Staffing v7 REST workflows. Worker list and single reads expose canonical worker names, employee IDs, work contacts and business titles. Worker WIDs are stable; single reads also accept `Employee_ID=...`. Lists default to 20 rows, accept `limit` up to 100 and `offset`, and exclude terminated employees unless `includeTerminatedWorkers=true`. Case-insensitive `search` matches name or worker ID, with space-separated terms combined using OR. For a returned worker ID, `/workers/{ID}/checkIns` and `/workers/{ID}/checkInTopics` support create, list, read, partial update and delete. Creation and ordinary PATCH requests use multipart form data containing one `jsonData` part with `Content-Type: application/json`. For example, the check-in JSON part is `{"date":"2026-09-06","description":"Weekly review"}`; a topic requires `name`. The response is JSON. Check-ins can reference existing topics through `associatedTopics: [{"id":"..."}]`; topics can reference check-ins through `associatedCheckIns`. Reads resolve both sides of those links from persisted records. DELETE removes the resource and its links and returns HTTP 200 with an empty `data` collection. Archive and restore use `PATCH .../{subresourceID}?type=archive` with an ordinary JSON body `{"archive":true}` or `{"archive":false}`. Only the creating Fictix credential can edit, archive or delete a check-in or topic. `privateNotes` are returned only to that credential and are omitted from generic record materialization. This workflow does not associate credentials with employees, so `me` and editing as a participant are unavailable. Standard Fictix idempotency applies to normalized multipart JSON, independent of its boundary. The implementation follows the [published Staffing v7 OpenAPI specification](https://developer.workday.com/bundles/rest-directory-ui/public/static/openApiFiles//staffing_v7_20260822_oas3.json) for these operations. It does not implement attachment uploads, other subresource reference IDs, supervisory visibility, metadata responses, tenant-prefixed paths, SOAP, hiring, payroll, compensation or job changes. Check-in writes do not alter employment or financial records. Unsupported fields and query options return errors. ## ADP Workforce Now workers and legal names Use `/api/v1/orgs/{slug}/native/adp` as the API base URL, with a Fictix API key. Writes require `Idempotency-Key`. This workflow follows ADP's published [Workforce Now guide](https://developers.adp.com/articles/preview/guide-worker-management-api-guide-for-adp-workforce-now-0) and [official request/response samples](https://github.com/adpllc/marketplace-sample-payloads/tree/master/wfn). These are documentation and wire-example checks; an official SDK or complete OpenAPI conformance is not claimed. `GET /hr/v2/workers` and `GET /hr/v2/worker-demographics`, including individual `/{associateOID}` reads, return canonical employee names, work email, job title, hire dates, department and manager references, and active/terminated status. Each worker has distinct stable associate, employee and position identifiers. Sensitive demographic fields, compensation and payroll histories are omitted. Lists support `$top` from 1 to 100, defaulting to 50, and nonnegative `$skip`. Reads after the last record return an empty 204. `$select` supports the implemented nested worker fields. A single `$filter` equality can select by worker ID, position ID, manager position ID, assignment status, or department code/type. `count=true` on `/workers` reports the unfiltered population in `meta.totalNumber`, following ADP's documented limitation. The only supported role is `Practitioner`. Unsupported queries, historical dates, asynchronous preferences and sorting options return errors. `POST /events/hr/v1/worker.legal-name.change` accepts one event containing `data.eventContext.worker.associateOID` and `data.transform.worker.person.legalName`. It updates `givenName`, `familyName1`, `middleName` and `nickName`. The completed event response is returned only after the canonical employee change persists. Subsequent ADP and other personnel provider reads reflect the change; retries with the same key replay the stored response. Invalid fields or unknown workers leave state unchanged. Onboarding, termination, scheduled changes, payroll, compensation, addresses, government identifiers, birth dates, code tables, photos, metadata endpoints and event-notification delivery are outside this workflow. No live ADP requests or financial changes occur. ## TriNet HR Platform The `trinet` native base implements the public HR Platform Core API, formerly Zenefits, for reading a generated company's personnel and organization. This is separate from TriNet PEO. Use your Fictix `X-API-Key` and replace the upstream `https://api.trinet.com` base with your company's `/native/trinet` base. Start with `GET core/companies` or `GET core/people`. The response contains `data.data`, `data.next_url`, `object: "/meta/response"`, numeric `status: 200` and the published string `error: "null"`. A detail request such as `GET core/people/{id}` contains the person directly in `data`. Follow returned company, department, manager, subordinate and employment URLs within your Fictix company. IDs are numeric strings; a person's API `id` is distinct from `employee_number`. Supported routes are company, people, department and employment lists/details, company people/departments, department people, and person subordinates/employments. Lists default to 20 rows, accept `limit` up to 100, and use exclusive `starting_after` or `ending_before` object IDs. Follow `next_url` for subsequent pages. Pagination IDs must belong to the current filtered collection; a roster change can invalidate one. Results are ordered by numeric API ID. People support exact `company`, `department`, `manager`, `first_name`, `last_name` and `status` filters. Company lists accept `name`; departments accept `company` and `name`; employments accept `person`. Unknown filters are rejected. `includes` takes space-separated paths, for example `includes=company.departments department.company manager employments.person`. Expandable fields are company `departments`; people `company`, `department`, `manager`, `employments`; department `company`; and employment `person`. Dotted paths may be three levels deep, with at most ten paths and 2,000 rendered records. Expanded lists use the default 20-row page and provide a `next_url`. Other relationships remain references that you can follow separately. These reads reflect canonical personnel changes made through supported HR integrations such as Justworks. Each employee has one current employment projection with known hire/termination dates and employment type when available. No past employment, compensation, bank, tax, address, benefit or demographic facts are invented. Roster members default to active unless canonical status or `active=false` indicates otherwise. Department identity derives from its case-folded canonical label and changes when the label is renamed. PEO, payroll, Core writes, locations, installation/subscription flows, OAuth and webhooks are outside this workflow. Follow the returned `next_url` until it is `null`; the final page follows the published pagination guide. Sources: [Core people](https://developers.zenefits.com/reference/people), [companies](https://developers.zenefits.com/reference/company), [departments](https://developers.zenefits.com/reference/department), [employments](https://developers.zenefits.com/reference/employment), [pagination](https://developers.zenefits.com/reference/pagination), and [inline expansion](https://developers.zenefits.com/reference/inline-expansion). # ============================================================ # https://fictix-docs.vercel.app/for-agents/consuming-these-docs # ============================================================ # Consuming these docs This page is for the agent, not the human reading over its shoulder. It lists every machine-readable entry point into the Fictix docs and how to fetch each one. If you're pointing an agent at Fictix for the first time, see [AI agent integration](/resources/agent-integration) for copy-paste project files (`AGENTS.md`, a Claude Code skill, Cursor rules); this page only covers how to read the docs site itself. ## Read the rules file first `AGENTS.md` at the repo root is the brief: auth layers, canonical entities, pagination and idempotency rules, and links to everything else. Fetch it before crawling anything: ```bash curl -sS https://fictix-docs.vercel.app/AGENTS.md ``` `/agents.md` (lowercase) resolves to the same file, for tools that assume the lowercase convention: ```bash curl -sS https://fictix-docs.vercel.app/agents.md ``` ## Fetch any page as Markdown Every page on this site is also served as plain Markdown — strip the styled HTML and append `.md` to the page's URL: ```bash curl -sS https://fictix-docs.vercel.app/api-reference/demo-jobs.md curl -sS https://fictix-docs.vercel.app/get-started/quickstart.md ``` Use this when you want one page's content in context without pulling in nav chrome, or when you're writing a fetch loop that turns a list of paths into a list of Markdown documents. ## Use the llms.txt index `/llms.txt` is a curated index of the site, grouped by section, with a one-line description per page — read this to decide what to fetch next instead of guessing from the sidebar: ```bash curl -sS https://fictix-docs.vercel.app/llms.txt ``` `/llms-full.txt` is every published page concatenated into a single file. Fetch it when you'd rather load the whole docs set once than crawl page by page: ```bash curl -sS https://fictix-docs.vercel.app/llms-full.txt ``` > **Note:** `llms-full.txt` is large. If your context window is tight, read `llms.txt` > first and fetch only the pages relevant to the task with `.md`. ## robots.txt `/robots.txt` allows all crawling and points back at the same three entry points, so a generic crawler or an agent that only checks `robots.txt` still finds the machine-readable paths: ```bash curl -sS https://fictix-docs.vercel.app/robots.txt ``` ## Quick reference | Want | Fetch | | --- | --- | | The rules brief | `https://fictix-docs.vercel.app/AGENTS.md` (or `/agents.md`) | | One page, as Markdown | `https://fictix-docs.vercel.app/.md` | | An index of the whole site | `https://fictix-docs.vercel.app/llms.txt` | | The whole site, concatenated | `https://fictix-docs.vercel.app/llms-full.txt` | | Crawl/entry-point pointers | `https://fictix-docs.vercel.app/robots.txt` | # ============================================================ # https://fictix-docs.vercel.app/for-agents/mcp # ============================================================ # MCP server The Fictix backend includes `fictix_mcp`, an MCP server that adapts the Fictix API into tools, resources, and prompts an MCP-aware agent can call directly, instead of the agent hand-rolling `curl` calls from [AGENTS.md](/for-agents/consuming-these-docs). It's a companion to the project-file snippets on [AI agent integration](/resources/agent-integration) — use those if you just want an `AGENTS.md`/skill file, use this if your client speaks MCP. > **Note:** [Download the standalone MCP adapter](/downloads/fictix-mcp-0.1.1.zip). > It installs independently of the backend repository and includes the public > API guides. Use a company you create in Console and its normal API key. > Native and execute tools retain the API's plan and scope restrictions. > **Warning:** Never put `FICTIX_PROJECT_API_KEY` or any other Fictix credential in browser > JavaScript or commit it to a repo. Configure the server with environment > variables and keep it running server-side or on your own machine, the same as > any other project API key. ## What it exposes - **Tools** that wrap the REST surface — reading entities and pages, discovering the catalog, and creating/controlling demo jobs. - **Resources** for pulling Fictix data (an entity page, a catalog listing) into an agent's context without a manual tool call. - **Prompts** that package common Fictix tasks — for example, loading a generated company into a target app — into a reusable starting point. - **Read/write safeguards**: read-only tools operate under the same project-scoped `X-API-Key` as the REST routes they wrap. Tools that create or control a company follow the same rules as `POST /api/v1/demo-jobs` directly — approved control-plane access (`FICTIX_ADMIN_API_KEY`, see below) and a stable `Idempotency-Key` — so running the server doesn't by itself grant write access you don't already have. ## Install and run Use Python 3.12 or later in a dedicated environment: ```bash python3.12 -m venv .venv .venv/bin/python -m pip install https://fictix-docs.vercel.app/downloads/fictix-mcp-0.1.1.zip .venv/bin/python -m fictix_mcp --help ``` The [archive manifest](/downloads/fictix-mcp-0.1.1.manifest.json) lists its SHA-256 and reviewed source files. This direct download is the supported installer; there is no PyPI or npm registry release yet. Do not substitute `pip install fictix-mcp`, `uvx fictix-mcp`, or `npx fictix-mcp`. After configuring the three environment variables below, run: ```bash .venv/bin/python -m fictix_mcp ``` `stdio` is the default transport. Your MCP client spawns the process and talks through stdin/stdout. Use the absolute path to this environment's Python in client configuration; a bare `python` can select an environment without the adapter. On Windows, the environment's executable is `.venv\Scripts\python.exe`. Start with `project_info`, then `browse_entity` with `entity="bills"`, `limit=25`, and `offset=0`. Page until the returned count is exhausted. The `fictix://docs/quickstart` resource and `search_docs` work from bundled public documentation, without a backend checkout. Streamable HTTP and SSE are optional local transports: `--transport streamable-http` or `--transport sse`, with `--host` and `--port`. Keep the default loopback host unless you have configured access control for other clients. ## Configure `fictix_mcp` reads its own environment variable names — they are **not** the `FICTIX_API_ORIGIN`/`FICTIX_PROJECT_SLUG` shell conventions the curl examples elsewhere in these docs use. The values are the same ones, from the same place — your project's **Connect to your builder** panel in the [Fictix Console](https://console.fictix.ai) — but the server only reads these names: ```bash FICTIX_BASE_URL # API origin, e.g. https://api.fictix.ai or https://api-staging.fictix.ai FICTIX_DEFAULT_SLUG # project slug, from Console > Connect to your builder FICTIX_PROJECT_API_KEY # from Console > Company settings > Company API keys ``` One optional variable enables the management tools (creating or controlling companies via demo jobs). It is for Fictix-approved server-to-server integrations only — a normal Console customer does not have it and cannot self-serve it: ```bash FICTIX_ADMIN_API_KEY # approved management key, sent as X-Admin-Key ``` The signed-in Console uses a managed WorkOS bearer session for its own management actions. That session is not available as a copyable MCP credential; external server-to-server MCP management uses the Fictix-supplied `FICTIX_ADMIN_API_KEY` only. If it is not set, the read-only tools still work under the project API key — management tool calls go out uncredentialed and come back with the same `401` that `POST /api/v1/demo-jobs` itself returns. ## Client config ### Claude Code ```bash claude mcp add fictix \ -e FICTIX_BASE_URL=https://api.fictix.ai \ -e FICTIX_DEFAULT_SLUG= \ -e FICTIX_PROJECT_API_KEY= \ -- /absolute/path/to/.venv/bin/python -m fictix_mcp ``` ### Codex Add a `[mcp_servers.fictix]` block to `~/.codex/config.toml`: ```toml [mcp_servers.fictix] command = "/absolute/path/to/.venv/bin/python" args = ["-m", "fictix_mcp"] env = { FICTIX_BASE_URL = "https://api.fictix.ai", FICTIX_DEFAULT_SLUG = "", FICTIX_PROJECT_API_KEY = "" } ``` See the [official Codex MCP configuration reference](https://learn.chatgpt.com/docs/extend/mcp?surface=cli) for command and environment settings. ### Cursor Add a `.cursor/mcp.json` in your project (or user config directory): ```json { "mcpServers": { "fictix": { "command": "/absolute/path/to/.venv/bin/python", "args": ["-m", "fictix_mcp"], "env": { "FICTIX_BASE_URL": "https://api.fictix.ai", "FICTIX_DEFAULT_SLUG": "", "FICTIX_PROJECT_API_KEY": "" } } } } ``` ## Sidecar today, not a hosted endpoint `fictix_mcp` is a process your agent (or you) run locally or on your own infrastructure — there is no hosted `mcp.fictix.ai`-style endpoint to point a client at instead. If your workflow needs a shared, always-on MCP endpoint rather than a per-developer local process, that's a gap worth raising with your Fictix contact; it isn't available yet. # ============================================================ # https://fictix-docs.vercel.app/for-agents/skill-pack # ============================================================ # Skill pack The Fictix skill pack is a small folder of plain-text files that teaches an agent the Fictix API without it re-deriving conventions from this whole site: a skill definition, a copy of the repo-level rules file, and a set of working examples. It's more than the single skill snippet on [AI agent integration](/resources/agent-integration) — that page shows you how to write your own minimal skill by hand; this pack is the version Fictix publishes and maintains directly, ready to drop in. ## What's inside | File | Purpose | | --- | --- | | [`SKILL.md`](/fictix-skill-pack/SKILL.md) | Skill frontmatter plus a distilled brief: auth layers, canonical entities, read/paginate/export conventions, error handling. | | [`AGENTS.md`](/fictix-skill-pack/AGENTS.md) | A copy of the repo-root [`AGENTS.md`](/AGENTS.md) — the full brief, unabridged. | | [`examples.md`](/fictix-skill-pack/examples.md) | Working `curl`/`jq` examples: reading data, company info, catalog discovery, CSV export, idempotent demo-job creation, and staging organization keys. | Each file is a plain download — fetch it directly, or have an agent fetch it for you: ```bash curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/SKILL.md curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/AGENTS.md curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/examples.md ``` > **Warning:** None of these files should ever contain a real API key. Read > `FICTIX_PROJECT_API_KEY` or `FICTIX_ORGANIZATION_API_KEY` from the environment at runtime; treat a committed > `SKILL.md` or `AGENTS.md` like any other source file a teammate — or the > internet — can read. ## Install for Claude Code Claude Code loads a skill from a folder under `.claude/skills/`. Create the folder and copy the pack's files into it: ```bash mkdir -p .claude/skills/fictix curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/SKILL.md -o .claude/skills/fictix/SKILL.md curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/AGENTS.md -o .claude/skills/fictix/AGENTS.md curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/examples.md -o .claude/skills/fictix/examples.md ``` Claude Code discovers `.claude/skills/fictix/SKILL.md` automatically and loads the rest of the folder as supporting files the skill can reference. ## Install for other agents Most other coding agents (Cursor, Codex, and similar tools) read a plain `AGENTS.md` from the repo root instead of a skills folder. Drop the pack's `AGENTS.md` there directly: ```bash curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/AGENTS.md -o AGENTS.md ``` If you already have a repo-root `AGENTS.md`, append the Fictix section instead of overwriting your existing rules. Keep `examples.md` nearby (for example alongside the `AGENTS.md` you just saved) so an agent that wants a worked example can find one without leaving the repo. ## Keeping it current The pack is a snapshot of the same source Fictix documents everywhere else — `AGENTS.md` and the canonical entity list in particular can change as the API evolves. Re-run the `curl` commands above periodically, or point an agent at [`/llms-full.txt`](/llms-full.txt) for the full current docs set instead of relying on a pack fetched once and never refreshed. # ============================================================ # https://fictix-docs.vercel.app/resources/rate-limits-and-quotas # ============================================================ # Rate limits and quotas This page documents the operational limits an API client should design around. Some values are environment-configurable, so client code should read response errors and headers instead of hard-coding every number. ## Request rate limiting The default API rate limit is: ```text 120 requests per 60 seconds ``` The backend reads these environment variables: | Setting | Default | Meaning | | --- | ---: | --- | | `RATE_LIMIT_ENABLED` | `true` | Enables request rate limiting. | | `RATE_LIMIT_REQUESTS_PER_MINUTE` | `120` | Requests allowed per bucket window. | | `RATE_LIMIT_WINDOW_SECONDS` | `60` | Window size in seconds. | | `RATE_LIMIT_BACKEND` | `auto` | Uses Redis when configured, otherwise memory. | | `RATE_LIMIT_REQUIRE_REDIS` | production dependent | Fails startup/readiness if Redis is required but unavailable. | When rate limited, clients receive: ```http 429 Too Many Requests ``` > **Note:** Clients should back off using the retry hint returned by the deployment when > available. If no header is present, use exponential backoff with jitter. ## Pagination limits Entity browsing uses bounded pagination: **GET** `/api/v1/demo-jobs/{job_ref}/entities/{entity}` ```text GET /api/v1/demo-jobs/{job_ref}/entities/{entity}?limit=100&offset=0 ``` Current maximum page size: ```text 1000 records ``` Clients should page until: ```text offset + data.length >= count ``` Do not assume that a single page can contain a full ledger. ## Partner bundle limit The normalized bundle endpoint is capped so one response cannot accidentally ship an entire large tenant through a JSON response. Default: ```text PARTNER_BUNDLE_MAX_RECORDS=25000 ``` Endpoint: **GET** `/api/v1/demo-jobs/{job_ref}/bundle` ```http GET /api/v1/demo-jobs/{job_ref}/bundle ``` If the selected entities exceed the cap: ```http 413 Payload Too Large ``` ```json { "detail": "Bundle too large for one response. Requested 30001 records across 15 entities, which exceeds the partner bundle cap of 25000. Use the entity or export endpoints instead." } ``` > **Warning:** The cap can be changed by deployment configuration. Treat `413` as a signal to > page or export, not as a numeric limit to hard-code into a client. Recovery options: - request fewer entities with repeated `entity` query params - use paginated entity reads - use per-entity exports - use the runtime CSV export pack route when the use case is "download all data" ## Dataset storage limits The current project dataset blob model has write-time guardrails. Defaults: | Setting | Default | | --- | ---: | | `FICTIX_MAX_PROJECT_DATASET_RECORDS` | `300000` | | `FICTIX_MAX_PROJECT_DATASET_BYTES` | `134217728` | If a write would exceed the dataset budget, the API returns a structured error: ```json { "detail": { "code": "project_dataset_limit_exceeded", "project_id": "proj_123", "context": "simulation_step", "record_count": 315000, "max_records": 300000, "estimated_bytes": 140000000, "max_bytes": 134217728, "message": "Project dataset exceeds current blob storage safety budget" } } ``` Client action: - reduce generated scale - clone into smaller scenario-specific jobs - avoid long unbounded simulation runs - export and archive data before destructive reset if needed ## Generation admission limits Fictix estimates generation load before doing expensive work. Default generation budget: ```text MAX_WEIGHTED_GENERATION_LOAD=300000 ``` Weighted load is roughly: ```text estimated_records * quality_multiplier * connector_multiplier ``` Quality multipliers: | Quality preset | Multiplier | | --- | ---: | | `clean` | `1.0` | | `realistic` | `1.1` | | `messy` | `1.25` | | `stress_test` | `1.5` | Connector multiplier: ```text 1 + max(0, connector_count - 1) * 0.25 ``` Full system materialization uses at least 6 connector-equivalent units. If a request is too large: ```http 422 Unprocessable Entity ``` ```json { "detail": "Requested generation load exceeds the server budget (420,000 weighted units for about 145,000 estimated records, quality 'realistic', 8 connectors; budget 300,000). Reduce employees, months_of_history, customers_per_employee, or connector breadth. With the current shape, try <= 85 employees or <= 12 months_of_history." } ``` Client action: - lower `generation.employees` - lower `generation.months_of_history` - reduce `customers_per_employee` - select fewer services - use `clean` for broad smoke tests - create multiple smaller demo jobs instead of one huge tenant ## Webhook limits Webhook subscription limits: | Limit | Value | | --- | ---: | | Active subscriptions per org | `20` | | Event filters stored per subscription | `50` | | Delivery worker batch size | `100` | | Synchronous POST attempts | `3` | Webhook delivery is billed/metered as `webhook_deliveries` when quota is configured for the project. ## Plan quotas Plan-level quotas are enforced from the billing catalog. The signed-in Fictix Console is the customer-facing source of truth for an organization's active plan and current usage; do not infer entitlement from a key name or a documentation table alone. Current default catalog limits: | Plan | Test runs/mo | API units/mo | Scenario runs/mo | Events/mo | Storage | Webhook deliveries/mo | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | Starter | 25 | 10,000 | 100 | 100,000 | 500 MB | 0 | | Hobbyist | 100 | 25,000 | 250 | 250,000 | 1 GB | 0 | | Developer | 500 | 100,000 | 5,000 | 2,000,000 | 10 GB | 25,000 | | Pro | 2,500 | 500,000 | 25,000 | 10,000,000 | 50 GB | 100,000 | | Enterprise | 10,000 | 2,500,000 | 100,000 | 50,000,000 | 100 GB | 250,000 | > **Warning:** When quota is exhausted, clients should stop the workflow or surface an upgrade > path. Do not auto-retry quota failures. Use the Console to review usage and > upgrade eligibility. ## Client backoff policy Recommended general policy: | Response | Retry? | Policy | | --- | --- | --- | | `429` | Yes | Respect retry hint; otherwise exponential backoff with jitter. | | `500` | Sometimes | Retry idempotent operations only. | | `503` | Yes | Back off and recheck readiness. | | `409` | No | Resolve idempotency conflict or active-operation conflict. | | `413` | No | Page or use exports instead. | | `422` | No | Fix request shape or reduce scale. | **POST** `/api/v1/demo-jobs` For `POST /api/v1/demo-jobs`, retry only with the same request body and same `Idempotency-Key`. # ============================================================ # https://fictix-docs.vercel.app/resources/versions-and-compatibility # ============================================================ # Versions and compatibility Fictix has two distinct versions that integration teams should track. | Version | What it identifies | Where to read it | | --- | --- | --- | | API deployment release | The deployed backend build | `/ready` deployment metadata and release notes | | Partner contract version | The normalized external response contract | `contract_version` in partner responses and `/openapi.json` | ## Current partner contract The current partner contract version is: ```text 2026-07-17 ``` > **Note:** It is a date-based string. Do not coerce it to a number. ## Compatibility rules - New optional fields and new catalog items can be added without changing the contract version. - Clients must ignore response fields they do not recognize. - A renamed, removed, or behaviorally incompatible public field/route requires a contract-version change and a changelog entry with migration guidance. - Pin your integration tests to a staging company and record the deployed release, partner contract version, selected services, and catalog response. The OpenAPI document specifies supported request bodies, auth schemes, and stable top-level response envelopes. Generated company records remain flexible by design; inspect the catalog for the selected service/object schema. ## Upgrade checklist Before moving an integration to a new Fictix release: 1. Read the [Changelog](/resources/changelog). 2. Fetch the staging `/openapi.json` and compare the partner paths you use. 3. Re-run your create/load/export or runtime test against a staging company. 4. Verify error handling still accepts both string and structured `detail`. 5. Store the release and `contract_version` with your test result. ## Deprecation discipline Fictix will document a replacement route/field and a client action before a public contract removal. Internal project/admin routes are not part of this compatibility commitment unless they are explicitly added to these developer docs. For Console-created companies, use the [Console onboarding flow](/get-started/console-onboarding) to rotate keys and test a staging integration before changing production. # ============================================================ # https://fictix-docs.vercel.app/resources/production-checklist # ============================================================ # Partner integration checklist Work through this checklist before your integration relies on Fictix in a real workflow. It covers what "production-ready" means for the API contract, auth and secrets, data quality, exports, simulation and runtime, and webhooks — the areas an integration team needs to confirm before launch. ## API contract - `/api/v1/demo-jobs` is the primary external surface. - `/api/v1/catalog/*` endpoints return current service, entity, capability, and export-pack metadata. - `/api/v1/demo-jobs/{job_ref}/status` returns generated status, company summary, dataset summary, quality summary, and simulation summary. - `/api/v1/demo-jobs/{job_ref}/quality` returns validation checks and a certificate. - `/api/v1/demo-jobs/{job_ref}/runtime-readiness` reports static, trickle, and continuous-readiness state. - `/api/v1/demo-jobs/{job_ref}/entities/{entity}` supports canonical entity aliases. - `/api/v1/demo-jobs/{job_ref}/entities/{entity}/export` supports JSON and CSV. - `/api/v1/demo-jobs/{job_ref}/bundle` fails with `413` rather than returning oversized responses. - Runtime/native routes remain documented as advanced routes, not as the first integration path. ## Auth and secrets - Customer onboarding begins in the Fictix Console, where project keys are created, rotated, and revoked. - Management auth is enabled for creating and controlling demo jobs. - Runtime API keys are generated per project. - Runtime API key prefixes are safe for display; full keys are not. - API keys are never written to frontend logs. - Webhook secrets are accepted on create and never returned. - Staging and production use separate credentials. - Local development credentials are not used in staging docs or examples. - Secret values are redacted from request, response, and error telemetry. ## Data quality - Generated counterparty domains use reserved synthetic namespaces such as `.example`, `.test`, `.invalid`, or a Fictix-owned fake namespace. - Entity counts in project metadata match fetchable entities. - Canonical ledgers do not duplicate alias ledgers in export packs. - `payments` is treated as an alias of `customer_payments`. - `bill_payments` is treated as an alias of `vendor_payments`. - `bank_transactions.bank_account_id` resolves to an exported bank account. - invoice line `item_id` resolves to `items`. - bill line `item_id` resolves to `items`. - journal entries balance within a one-cent tolerance. - invoice totals reconcile against amount paid and balance. - bill totals reconcile against amount paid and balance. - payroll gross, tax, benefit, and net pay reconcile. - validation certificates are generated and exposed. ## Export readiness - Runtime CSV export pack returns a real ZIP file. - ZIP contains all canonical ledgers that the UI advertises. - ZIP includes a README or schema document. - ZIP row counts reconcile against `record_counts`. - Duplicate alias files are omitted or explicitly documented. - Normalized line files are included when available: - `invoice_lines.csv` - `bill_lines.csv` - `journal_entry_lines.csv` - `payment_applications.csv` - CSV exports preserve stable headers. - JSON-in-CSV cells are documented when unavoidable. - Empty ledgers are either included intentionally or excluded consistently. ## Simulation and runtime - Simulation can start from a generated job. - Simulation status returns current simulated time, mode, and state. - Advancing time respects maximum advance limits. - Step, pause, resume, dials, checkpoint, and restore routes are covered by tests. - Events are written when simulation or scenario actions mutate state. - Runtime reads remain tenant-scoped by project. - Native-shaped routes for supported services return deterministic data for the same seed and company state. - Runtime writes either mutate state consistently or return a documented unsupported-operation response. ## Webhooks - Webhook creation requires the `webhooks` entitlement. - Callback URLs are SSRF-guarded. - Subscription secrets are not returned. - Delivery payloads are HMAC-SHA256 signed when a secret is configured. - Delivery worker is running in staging/production. - Durable outbox tables exist and migrations are applied. - Duplicate delivery is possible and documented. - Test event delivery works against a known receiver. - Delivery failures are visible in telemetry. ## Observability - Errors include request ids or trace ids when available. - Webhook delivery failures include project id, event type, and sanitized error. ## Partner launch gate Do not launch a partner integration until all of the following are true: - create job works in staging with management auth - Console-created project can create/rotate a runtime key and make a server-side runtime request in staging - status reaches `ready` - quality certificate is present - entity pagination works for every advertised canonical entity - CSV export works for every advertised canonical entity - full CSV export pack downloads as ZIP - runtime API key can read native-shaped data for selected services - webhooks can deliver a signed test event - simulation can advance and produce inspectable events - test matrix and test run flows complete - the docs site points to the same contract version as the deployed API # ============================================================ # https://fictix-docs.vercel.app/resources/agent-integration # ============================================================ # AI agent integration Many teams building against Fictix now do most of the integration work through an AI coding agent — Claude Code, Cursor, Codex/OpenAI Codex, or something similar — rather than hand-writing every request. Agents work best when they have a short, explicit brief instead of having to infer conventions from a full doc site, so this page gives copy-paste starting points for pointing your agent at Fictix: a docs-ingestion trick, and ready-made project files for Claude Code and Cursor. ## Machine-readable docs Every page on this site is available as raw Markdown — useful for pasting into a prompt or letting an agent fetch it directly: - Append `.md` to any page URL to get that page as plain Markdown, e.g. `https://fictix-docs.vercel.app/api-reference/demo-jobs.md`. - [`/llms.txt`](/llms.txt) is a curated index of the docs, meant to help an agent decide what to fetch next. - [`/llms-full.txt`](/llms-full.txt) is the entire documentation set concatenated into one file, sized for one-shot ingestion into an agent's context window when you'd rather hand over everything up front than let the agent crawl page by page. > **Warning:** None of the snippets below should ever contain a real project API key. If you > paste one of these files into a repo that's shared or public, read the key > from an environment variable at runtime instead of hardcoding it — treat a > committed `AGENTS.md`, `SKILL.md`, or `.cursor/rules/*.mdc` file exactly like > any other source file a teammate (or the internet) can read. ## AGENTS.md Drop a condensed version of this into your own project's `AGENTS.md` (or append it to an existing one) so any agent that reads that file picks up the essentials before it writes a single request. ````markdown ## Fictix API Fictix is a synthetic-company data API (accounting, banking, CRM, payroll, tasks). Get a project slug and API key from the Fictix Console (`console.fictix.ai` → Company settings → Company API keys) — never invent or construct these values. Environment variables this project expects: ```bash export FICTIX_API_ORIGIN="https://api.fictix.ai" # or api-staging.fictix.ai export FICTIX_PROJECT_SLUG="" export FICTIX_PROJECT_API_KEY=" Company settings > Company API keys>" ``` Auth header for all runtime/data requests: ```http X-API-Key: ``` Runtime data lives under `GET $FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/{entity}`. Canonical entity names — use these, not the deprecated aliases in parentheses: accounts, bank_accounts, bank_transactions, bills, credit_memos, customers, customer_payments (alias: payments), deals, employees, invoices, items, journal_entries, payroll_runs, tasks, vendor_payments (alias: bill_payments), vendors Page with `limit`/`offset` and continue while `offset + data.length < count`. If this project also creates/controls companies via `POST /api/v1/demo-jobs` (approved control-plane access only, separate from the project API key above), use the Fictix-supplied `FICTIX_ADMIN_API_KEY` as `X-Admin-Key`. The signed-in Console uses a managed WorkOS bearer session for its own actions; do not ask for or copy that session token. Every call must carry an `Idempotency-Key` header. Reuse the same key only to retry the exact same request body — a different body with a reused key returns `409`. Never reuse a placeholder key like `demo` or `test` across unrelated calls. Full reference: https://fictix-docs.vercel.app/llms-full.txt ```` ## Claude Code skill Save this as `.claude/skills/fictix-api/SKILL.md` in your own repo to teach Claude Code these facts as a loadable skill instead of relying on it re-deriving them from context each time. ````markdown --- name: fictix-api description: Facts and conventions for calling the Fictix synthetic-company API — base URL, auth header, and canonical entity names. --- # Fictix API Fictix serves synthetic-company data (accounting, banking, CRM, payroll, tasks) for a company created or selected in the Fictix Console. ## Config Read these from the environment — never hardcode a real key in this file or in generated code: ```bash FICTIX_API_ORIGIN # e.g. https://api.fictix.ai or https://api-staging.fictix.ai FICTIX_PROJECT_SLUG # from Console > Connect to your builder FICTIX_PROJECT_API_KEY # from Console > Company settings > Company API keys ``` ## Auth Runtime/data requests use a project API key in this header: ```http X-API-Key: ``` Scope it to `/api/v1/orgs/{FICTIX_PROJECT_SLUG}/...` routes only. Don't send this key to `/api/v1/demo-jobs` control-plane routes, and don't send a control-plane credential to `/orgs/{slug}` routes. The external control-plane credential (approved partners only) is `FICTIX_ADMIN_API_KEY` as `X-Admin-Key`; never invent other credential variable names or solicit a Console session token. ## Canonical entities Use these names (not the parenthetical legacy aliases) when reading or generating code against entity data: accounts, bank_accounts, bank_transactions, bills, credit_memos, customers, customer_payments (alias: payments), deals, employees, invoices, items, journal_entries, payroll_runs, tasks, vendor_payments (alias: bill_payments), vendors ## Common request ```bash curl -sS "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/customers?limit=100&offset=0" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` Page with `limit`/`offset`; continue while `offset + data.length < count`. ## Idempotency (control-plane only) `POST /api/v1/demo-jobs` requires a stable `Idempotency-Key` header. Reuse a key only to retry the identical request body; a different body with a reused key returns `409`. ## Further reading Full docs as Markdown: https://fictix-docs.vercel.app/llms-full.txt (or fetch a single page by appending `.md` to its URL). ```` ## Cursor rules Save this as `.cursor/rules/fictix.mdc` so Cursor applies it automatically whenever you're editing files that touch the Fictix integration. ```markdown --- description: Fictix API conventions — base URL, auth header, canonical entities, and idempotency rules for demo-jobs. globs: ["**/*fictix*", "**/lib/fictix/**"] alwaysApply: false --- # Fictix API conventions - Base URL and credentials come from the environment, never hardcoded: `FICTIX_API_ORIGIN`, `FICTIX_PROJECT_SLUG`, `FICTIX_PROJECT_API_KEY`. - Runtime/data requests authenticate with `X-API-Key: ` and are scoped to `/api/v1/orgs/{FICTIX_PROJECT_SLUG}/...`. - Use canonical entity names in new code: accounts, bank_accounts, bank_transactions, bills, credit_memos, customers, customer_payments, deals, employees, invoices, items, journal_entries, payroll_runs, tasks, vendor_payments, vendors. Treat `payments` and `bill_payments` as deprecated aliases for `customer_payments` and `vendor_payments`. - Page list endpoints with `limit`/`offset`; keep paging while `offset + data.length < count`. - Any call to `POST /api/v1/demo-jobs` must include a stable `Idempotency-Key` header. Only retry with the same key if the request body is unchanged — a changed body with a reused key returns `409`. - Error `detail` may be a plain string or a `{ code, message }` object; handle both shapes. - Fictix ships an official MCP sidecar (`python -m fictix_mcp`) — prefer it over hand-wrapping these REST endpoints. It is a local process, not a hosted endpoint. Setup: https://fictix-docs.vercel.app/for-agents/mcp ``` ## Every other coding agent — same brief, different filename The same three ingredients — the env vars, the `X-API-Key` header, and the canonical entity list — are the whole brief regardless of tool. The AGENTS.md snippet above is the content; the only thing that changes is which file your tool reads it from: | Tool | Where to put the brief | | --- | --- | | Codex, Amp, opencode, and most newer agents | `AGENTS.md` at the repo root — the emerging cross-tool standard | | GitHub Copilot | `.github/copilot-instructions.md`; the Copilot coding agent also reads `AGENTS.md` directly | | Windsurf | `.windsurf/rules/fictix.md` (legacy single-file: `.windsurfrules`) | | Cline | `.clinerules` (single file or folder) | | Gemini CLI | `GEMINI.md` | | JetBrains Junie | `.junie/guidelines.md` | | Replit Agent | `replit.md` | Or skip the copy-paste and pull the maintained version straight from the [skill pack](/for-agents/skill-pack): ```bash curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/AGENTS.md -o AGENTS.md ``` Most of these tools also speak MCP (Copilot in VS Code, Windsurf, Cline, Gemini CLI). The [MCP sidecar](/for-agents/mcp) (`python -m fictix_mcp`) works with all of them — each client has its own config file shape, but the server env vars are always the same ones documented on that page (`FICTIX_BASE_URL`, `FICTIX_DEFAULT_SLUG`, `FICTIX_PROJECT_API_KEY`). ## Browser-based app builders (Lovable, v0, Bolt, Replit) Prompt-driven app builders generate **frontend** code, and a Fictix project API key must never ship in frontend code — see [browser safety](/get-started/authentication). The pattern that works is a thin server-side proxy: the builder's server leg (Lovable → Supabase Edge Function, v0 → Next.js route handler, Bolt/Replit → server endpoint) holds the key as a secret and relays requests; the browser only ever talks to your proxy. Paste this into your builder's knowledge/system-prompt area: ```text This app reads business data from the Fictix API (synthetic-company data: accounting, banking, CRM, payroll, tasks). Rules: - Never call api.fictix.ai from browser code, and never put FICTIX_PROJECT_API_KEY in client-side source or public env vars. - Create a server-side proxy (edge function / route handler) that reads FICTIX_API_ORIGIN, FICTIX_PROJECT_SLUG, and FICTIX_PROJECT_API_KEY from server secrets. - The proxy calls GET {FICTIX_API_ORIGIN}/api/v1/orgs/{FICTIX_PROJECT_SLUG}/data/{entity} with header "X-API-Key: {FICTIX_PROJECT_API_KEY}" and returns the JSON to the browser. Support limit/offset query params. - Valid entities: accounts, bank_accounts, bank_transactions, bills, credit_memos, customers, customer_payments, deals, employees, invoices, items, journal_entries, payroll_runs, tasks, vendor_payments, vendors. - Page with limit/offset; keep fetching while offset + data.length < count. - Error "detail" may be a plain string or a { code, message } object — handle both shapes. Full API docs: https://fictix-docs.vercel.app/llms-full.txt ``` These tools can't run a local MCP sidecar, and there's no hosted MCP endpoint yet — the proxy brief above is the supported path today. # ============================================================ # https://fictix-docs.vercel.app/resources/changelog # ============================================================ # Changelog ## September 7, 2026 — Provider workflows in staging - The staging catalog reported 60 entries: 59 marked available/selectable and one FCI execute preview, which is not selectable in the standard connector picker. - The first-request directory covers every provider, including GraphQL, XML, form, and execute request bodies, and offers a downloadable JSON reference. - The connection guide covers API keys, SDK base URLs, operation scopes, idempotency, and provider-level errors. The workflow reference documents the supported operations and limits. - Public catalog snapshots observed October 28, 2026 report 60 entries in production as well as staging (59 marked available/selectable and one FCI preview). This is catalog metadata, not production request verification. Relay Financial remains unavailable pending a direct API contract. TriNet refers to HR Platform (formerly Zenefits), not PEO. See [Connect your software](/guides/connect-your-software), [Provider first requests](/api-reference/provider-first-requests), and [Provider workflows](/api-reference/provider-workflows). ## September 5, 2026 — Organization keys in staging - Organization admins can issue one API key for all current and future companies in their organization. - The company directory returns IDs and runtime slugs, with pagination for large organizations. - Keys support access scopes, expiry, rotation, revocation, and usage metadata. - Existing company keys remain available. Organization keys are staging-only in this release. See [One key for multiple companies](/get-started/organization-api-keys). This changelog tracks the public developer API contract described by `docs/fictix-api`. Internal backend changes should appear here only when they change external behavior, documented limits, or integration expectations. ## 2026-07-17 **Changed:** - `POST /api/v1/demo-jobs` now documents its typed ready-create envelope: `regeneration_required`, lifecycle timestamps, optional simulation/scenario summaries, and a settled credit receipt. - Create errors now distinguish invalid requests, management access, credit admission, idempotency/issuance conflicts, validation, and capacity limits. - The generated Company and Partner OpenAPI references are refreshed from the live production contract during documentation validation. **Client action:** - Accept the current create envelope and preserve `regeneration_required` with the job record. Regenerate a flagged company before a current-contract import. - Handle both the usual `detail` error envelope and the create-time credit envelope with `error` and `message`. **Contract:** - version: `2026-07-17` - tested against: the live production OpenAPI contract ## 2026-07-13 **Added:** - [AI agent integration](/resources/agent-integration) now maps the canonical agent brief to every major tool's instructions file (GitHub Copilot, Windsurf, Cline, Gemini CLI, JetBrains Junie, Replit Agent) instead of only Claude Code and Cursor. - A browser-based app builder section (Lovable, v0, Bolt, Replit) with a paste-ready knowledge prompt teaching the server-side proxy pattern — project API keys never ship in generated frontend code. **Contract:** - version: `2026-07-05` (unchanged — docs-only additions) ## 2026-07-12 **Fixed:** - The [MCP server](/for-agents/mcp) page documented env vars `fictix_mcp` never reads. The server reads `FICTIX_BASE_URL` and `FICTIX_DEFAULT_SLUG` (not `FICTIX_API_ORIGIN`/`FICTIX_PROJECT_SLUG`); the Claude Code, Codex, and Cursor config snippets are corrected. If you copied the old snippets, your sidecar was silently falling back to the production origin with no default slug — update your config. - Replaced the undefined `$FICTIX_ACCESS_TOKEN`/`$FICTIX_BASE_URL` names in the entities-and-exports and skill-pack examples with the real ecosystem names (`FICTIX_ADMIN_API_KEY`, `FICTIX_API_ORIGIN`). - Removed a stale claim on [AI agent integration](/resources/agent-integration) that Fictix ships no MCP server. **Changed:** - [Authentication](/get-started/authentication), [`AGENTS.md`](/AGENTS.md), and the skill pack now name the complete set of credential env vars — the three self-service values plus the Fictix-supplied `FICTIX_ADMIN_API_KEY` (approved partners only) — and explicitly instruct agents not to invent variable names or solicit Console session tokens. - [Start in the Console](/get-started/console-onboarding) now maps the Connect panel's variable names (`FICTIX_API_BASE_URL`, `FICTIX_API_KEY`) to the convention used across these docs. **Contract:** - version: `2026-07-05` (unchanged — docs-only corrections) - client action: if you configured `fictix_mcp` from the old MCP page snippets, rename `FICTIX_API_ORIGIN` → `FICTIX_BASE_URL` and `FICTIX_PROJECT_SLUG` → `FICTIX_DEFAULT_SLUG` in your MCP client config. ## 2026-07-11 **Changed:** - Documented the Console-first onboarding flow for project creation, project API key issuance, rotation, revocation, organization access, and billing. - Clarified that `X-Admin-Key` is an approved server-to-server control-plane credential, not a self-service frontend key. - Added explicit production and staging API origins. - Added response-envelope, capability-matrix, and version/compatibility references. - Added OpenAPI security metadata and typed top-level partner response envelopes, plus CI validation for docs/OpenAPI drift. > **Warning:** `X-Admin-Key` is an approved server-to-server control-plane credential. It is > not a self-service frontend key. **Fixed:** - Updated example `contract_version` values to the current date-based string. - Corrected the stale 5,000-record bundle-cap example to the documented default of 25,000 records. **Contract:** - version: `2026-07-05` - client action: treat `contract_version` as a string and begin onboarding in the Fictix Console. ## 2026-07-06 **Added:** - The ambulatory surgery center (`asc_surgical`) clinical content pack: operating rooms, synthetic surgical cases, clinical incidents, and dictated incident scripts with extraction answer keys. - Five ASC scenario templates for extraction and compliance-style evaluation: ambiguous time references, duplicate staff names, unstated harm levels, mid-sentence corrections, and late reporting gaps. **Client action:** - Select **Ambulatory Surgery Center** in the Console, or set `company.industry` to `asc_surgical` for an approved server-to-server create. - Treat the pack as synthetic evaluation data only. It contains no real patient data and is not for clinical decision-making. **Contract:** - version: `2026-07-05` - tested commit: `5f367d2` ## 2026-07-01 Initial developer-docs source directory. **Added:** - start-here overview - copy-paste quickstart - authentication model - demo job concept docs - company blueprint docs - entity, lineage, and count docs - simulation concept docs - catalog API reference - demo jobs API reference - entity and export API reference - simulation, scenario, and event API reference - test matrix and test run API reference - runtime and native routes API reference - webhook API reference - error, limit, and idempotency reference - build-a-demo-tenant guide - load-into-your-app guide - evaluate-your-app guide - rate limit and quota resource - production checklist - OpenAPI and docs-site publishing guide **Contract notes:** - `/api/v1/demo-jobs` is the preferred external API surface. - `payments` is documented as an alias of `customer_payments`. - `bill_payments` is documented as an alias of `vendor_payments`. - Canonical entity docs expect alias ledgers not to be double-counted. - Runtime/org/native routes are advanced routes, not the first integration path. - Webhooks are documented as feature-entitled, signed, SSRF-guarded, and async. **Operational notes:** - Public docs should be generated from this Markdown plus `/openapi.json`. - Docs CI should fail on route drift, broken links, and contract-version changes without a changelog update. ## Changelog policy Use these labels: | Label | Meaning | | --- | --- | | Added | New endpoint, field, entity, guide, or capability. | | Changed | Behavior changed without removing compatibility. | | Deprecated | Still works, but clients should migrate. | | Removed | No longer available. Requires migration notes. | | Fixed | Bug fix that affects documented behavior. | | Security | Auth, data isolation, signature, SSRF, or secret-handling change. | Every entry should include: - affected endpoint or concept - old behavior - new behavior - client action - contract version - tested commit or release id ## Breaking change template ```md ## YYYY-MM-DD Changed: - `GET /api/v1/demo-jobs/{job_ref}/entities/{entity}` now returns ... Client action: - Update parsers to read ... Compatibility: - Old field remains until ... Contract: - version: 2026-... - tested commit: ... ``` Developer platform / Synthetic company infrastructure # Test your product against a company you can know. Fictix generates a believable, reconciled company across accounting, banking, CRM, payroll, and tasks. Plant known issues, load the data into your product, and measure what it catches—without using real customer data. [Generate a company ↗](https://console.fictix.ai/auth/sign-up?next=%2Forgs)[Read the quickstart →](/get-started/quickstart) First runtime request ## Read a generated company's customers Create or select a company in the Console, then copy its project slug, runtime origin, and project API key from **Connect to your builder**. [Start in the Console →](/get-started/console-onboarding)[Connect a provider API or SDK →](/guides/connect-your-software) curlGET · PROJECT KEY ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/customers?limit=10" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` bash 01**Synthetic by construction**No customer data is ingested. 02**Reconciled across systems**The ledger, bank, CRM, and payroll agree. 03**Ground truth included**You know exactly what your product should find. Build paths ## Go from company to test result Choose the path that matches the job in front of you. [01 / Connect**Connect a company**Use the Console, keep the project key server-side, and make your first runtime read._Start here →_](/get-started/overview)[02 / Load**Load believable data**Page normalized entities, export ledgers, or consume native-shaped connector records._Choose a shape →_](/guides/load-into-your-app)[03 / Evaluate**Evaluate your product**Plant scenarios, preserve the manifest, and score your product against known outcomes._Measure a result →_](/guides/evaluate-your-app) Executable contracts ## Guides and API reference, in one place The narrative docs explain the integration decisions. The Scalar references render the exact request, response, and schema contracts from curated OpenAPI documents. [Runtime / X-API-Key**Company API**Project-key runtime routes and credential-free capability discovery for Console-generated companies._Open reference →_](/api/company)[Control plane / Approved access**Partner API**Server-to-server demo jobs, simulation, test matrices, exports, and lifecycle controls._Open reference →_](/api/partner) **Console first.**A normal customer does not create companies through the Partner API. Fictix never asks you to extract a bearer token from a Console session. Keep exploring ## The rest of the system [Concept**Entities and lineage**See how records stay coherent across accounting, banking, CRM, and payroll._Understand the model →_](/concepts/entities-and-lineage)[Healthcare**ASC clinical data**Generate a synthetic surgery-center dataset with known safety-event ground truth._Explore the pack →_](/concepts/clinical-asc)[Agents**Build with an agent**Use llms.txt, raw Markdown routes, AGENTS.md, or the Fictix skill pack._Read agent docs →_](/for-agents/consuming-these-docs)[Production**Ship the integration**Review credentials, pagination, retries, compatibility, observability, and rollout checks._Run the checklist →_](/resources/production-checklist) # Overview Fictix is an API for generating believable synthetic companies and using them to test software products. A Fictix company can include accounting data, banking activity, customer and vendor records, payroll, CRM data, tasks, native-shaped connector records, a simulation clock, webhooks, and planted scenarios for evaluating whether another application catches known business issues. The most important developer idea is simple: ```text You create a demo job. Fictix creates a generated company. You fetch normalized data, exports, events, or native-shaped records. You load or test that data in your own product. ``` text ## Start in the Console The Console is the front door for customers. [Create an account](https://console.fictix.ai/auth/sign-up?next=%2Forgs) or [sign in to projects](https://console.fictix.ai/auth/sign-in?next=%2Fprojects), then [generate a company](https://console.fictix.ai/generate) or select an existing one. Use its **Connect to your builder** panel or [Company settings](https://console.fictix.ai/settings/company) to obtain the runtime URL and project key for server-side code. This keeps company creation, organization access, billing, key rotation, and webhook administration in one customer-facing place. ![](phosphor/regular/info) See [Start In The Fictix Console](/get-started/console-onboarding) before following an API quickstart. ## What you can build Use Fictix when you need to: * create a realistic demo tenant without exposing real customer data * populate a B2B SaaS product with believable business activity * generate repeatable accounting, banking, CRM, payroll, task, and commerce data * test ingestion pipelines against coherent cross-system records * run scenario-based QA with planted truth, such as ghost employees, delayed payments, duplicate vendors, or reconciliation issues * give AI agents or eval harnesses a synthetic company to inspect * reproduce a customer-like state for demos, onboarding, QA, or sales engineering ## The main API surface Approved server-to-server integrations use this normalized control-plane surface: POST`/api/v1/demo-jobs` ```text POST /api/v1/demo-jobs GET /api/v1/demo-jobs/{job_id} GET /api/v1/demo-jobs/{job_id}/status GET /api/v1/demo-jobs/{job_id}/bundle GET /api/v1/demo-jobs/{job_id}/entities/{entity} GET /api/v1/demo-jobs/{job_id}/entities/{entity}/export ``` text The `demo_job` resource wraps the lower-level project/runtime implementation and returns stable links, company metadata, dataset counts, quality status, and runtime identifiers in one place. ## Base URLs Use the base URL for the environment you are targeting: ```text Production: https://api.fictix.ai Staging: https://api-staging.fictix.ai Runtime handoff: https://staging.fictix.dev/{publish_id}/{service}/... ``` text If your deployment uses a different host, keep the path structure the same and replace only the origin. ## Authentication models Fictix has a public discovery surface, a Console-managed bearer session, and one external credentialed auth layer: | Layer | Used for | Header | | ----------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | | Project runtime key | Connect an existing company to server-side code | `X-API-Key: ` | | Console bearer session | Signed-in Console management flows | WorkOS bearer session managed by the Console; never copy it | | External management key | Fictix-approved server-to-server control-plane integrations | `X-Admin-Key: $FICTIX_ADMIN_API_KEY` | ![](phosphor/regular/warning) Most customer integrations should use the project runtime key issued in the Console. The signed-in Console can create or control demo jobs with its managed WorkOS bearer session. External integrations may do so only with the Fictix-supplied admin key above. The management surface is not a self-service browser-key flow, and a Console session must never be copied or forwarded. ## Response objects Fictix responses use object names so clients can branch safely: ```json { "object": "demo_job", "contract_version": "2026-07-17", "id": "8f3a2d11", "status": "ready" } ``` json Common object names: * `demo_job` * `demo_job_status` * `demo_job_quality` * `demo_bundle` * `demo_entity_page` * `demo_entity_export` * `catalog_collection` * `test_matrix` * `test_run` ## Environment expectations For a production-grade integration, plan for: * idempotent demo job creation * retries around network failures and `5xx` responses * handling `413` bundle-size responses by paging entities * storing `job_id`, `runtime.project_id`, and `runtime.slug` * never storing plaintext runtime API keys unless you need lower-level runtime access * validating `quality.summary` before presenting generated data as clean * using reserved synthetic domains in generated counterparty data ## First integration decision Before writing code, choose one of these integration modes: | Mode | Use when | Main endpoints | | --------------- | ------------------------------------------------------------------------ | --------------------------------------------- | | Console runtime | You need an existing Console-created company in your app | runtime data, exports, native routes | | Static data | You have approved control-plane access and need a generated company once | create job, status, bundle, entity exports | | Data loader | You need to map Fictix into your own schema | catalog, entities, exports | | Runtime demo | You need ongoing simulated activity | simulation start/status/advance/events | | Eval harness | You need planted truth and scoring | scenarios, manifest, test matrices, test runs | | Native parity | You need API-shaped connector behavior | runtime/native routes after creating a job | # Start in the Fictix Console Fictix is Console-first. The Console is where a team creates a synthetic company, controls access, manages billing, and obtains the project-scoped key used to connect that company to server-side code. Open the frontend before making your first runtime API request: [create an account](https://console.fictix.ai/auth/sign-up?next=%2Forgs), or [sign in and open your projects](https://console.fictix.ai/auth/sign-in?next=%2Fprojects). ## 1. Sign in and select an organization Sign in, then create or select the organization that owns the test company. New users land in [Organizations](https://console.fictix.ai/orgs) after account creation; existing users can manage organizations at the same frontend route. Organization roles control who can create companies, manage keys, configure webhooks, and manage billing. ![](phosphor/regular/warning) Do not share a single organization across unrelated customers or environments. Use a separate organization or project for each isolated test-company context. ## 2. Create or select a test company Use [Generate a company](https://console.fictix.ai/generate) for a new company, or [open your projects](https://console.fictix.ai/projects) for an existing one. The Console owns the user-facing setup flow; API consumers do not need to construct an internal project record by hand. When generation completes, the project has: * a project id and slug * generated, synthetic company data * selected service capabilities * a project-scoped runtime API key * a Connect panel with copyable server-side setup details ## 3. Create a company API key In staging, the company's **API** page also lets you select an integration, issue a company key, and copy its first request. Follow [Connect your software](/guides/connect-your-software) for the expanded provider catalog and the correct JSON, XML, form, or execute transport. For one key covering all companies in an organization, staging admins and owners can instead use **Organization settings → Organization API keys**. See [One key for multiple companies](/get-started/organization-api-keys). Open [Company settings](https://console.fictix.ai/settings/company), then use **Company API keys** to create a key. Copy the secret when it is shown and store it in your server-side secret manager. The Console only displays the full key at creation or rotation time. Use the key with the generated company's runtime routes: ```http X-API-Key: ``` http ![](phosphor/regular/warning) Never put this key in browser JavaScript, a public environment variable, a mobile app, a screenshot, or a git repository. ## Console management session The signed-in Console uses a WorkOS-backed bearer session for its own management actions, including company creation and webhook administration. The Console manages that session in the app; it is not a copyable customer credential, has no environment variable for customers, and must not be forwarded from browser code. ## 4. Use the Connect panel The project's **Connect to your builder** panel gives you the runtime API origin, company slug, company API key, and a sample request. Use the values from that panel to configure your server-side integration: ```bash FICTIX_API_ORIGIN="https://api.fictix.ai" FICTIX_PROJECT_SLUG="" FICTIX_PROJECT_API_KEY="" ``` bash FICTIX\_API\_ORIGIN is the origin only; append the documented route path in your request. For staging, use and a matching staging company and key. Keep these values in a server-side secret manager or an ignored local environment file. Do not put the key in browser code or a public environment variable. ## 5. Rotate or revoke keys in the Console Project administrators can rotate or revoke keys from the same **Company API keys** area. Rotate a key when it may have been exposed; revoke it when the integration no longer needs access. Update your server-side secret before removing the old key from a live integration. ## Console destinations Every customer-facing setup task starts in the frontend. Link to these routes from a docs-site header, onboarding email, or product handoff instead of asking users to call a management API directly. | Need | Frontend destination | | --------------------------------------- | --------------------------------------------------------------------- | | Create an account | [Create account](https://console.fictix.ai/auth/sign-up?next=%2Forgs) | | Sign in and see projects | [Sign in](https://console.fictix.ai/auth/sign-in?next=%2Fprojects) | | Create a test company | [Generate](https://console.fictix.ai/generate) | | Open or select a company | [Projects](https://console.fictix.ai/projects) | | Create, rotate, or revoke a company key | [Company settings](https://console.fictix.ai/settings/company) | | Review organization access | [Organizations](https://console.fictix.ai/orgs) | | Manage billing or usage | [Billing](https://console.fictix.ai/billing) | | Explore exports and API setup | [Exports & API](https://console.fictix.ai/app/api) | ## Which API should I use? | Goal | Start here | Auth | | -------------------------------------------------------- | ------------------------------------------ | -------------------------------------------------------------------------- | | Connect an existing test company to your app | Runtime routes under `/api/v1/orgs/{slug}` | Project API key from the Console | | Discover available entities and services | `/api/v1/catalog/*` | No credential required | | Create or control companies in the Console | `/api/v1/demo-jobs` | Console-managed WorkOS bearer session (not copyable) | | Create or control companies from an external integration | `/api/v1/demo-jobs` | `FICTIX_ADMIN_API_KEY` / `X-Admin-Key` (Fictix-approved integrations only) | ![](phosphor/regular/warning) The `/demo-jobs` control plane is intentionally not a browser-key flow. The Console UI uses its managed WorkOS bearer session. For an approved backend-to-backend integration, use `FICTIX_ADMIN_API_KEY` as `X-Admin-Key`, supplied by Fictix. The Console's WorkOS bearer session is managed by the Console and is not an external integration credential. Never use email/password auth, copy a browser session token, or embed the admin key in a frontend application. Normal customers should use the project `X-API-Key` runtime flow above. Next: [Runtime And Native Routes](/api-reference/runtime-native-routes) or the [Quickstart](/get-started/quickstart). # Quickstart This quickstart connects an application to a synthetic company that already exists in Fictix. To connect many companies with one key, use the staging [organization-key quickstart](/get-started/organization-api-keys). The steps below remain the company-key path, supported in both production and staging. To call a provider API from your own software, follow [Connect your software](/guides/connect-your-software) and the [provider first requests](/api-reference/provider-first-requests). Public catalog snapshots observed on October 28, 2026 report 60 entries in both environments: 59 marked available/selectable and one FCI execute preview that is not selectable. These catalog reports are not production request tests or a strict parity claim; the published first-request templates were tested in staging only. ![](phosphor/regular/info) Start in the frontend — [create an account](https://console.fictix.ai/auth/sign-up?next=%2Forgs) or [sign in to projects](https://console.fictix.ai/auth/sign-in?next=%2Fprojects) — not by putting a management credential into a browser or a local shell history. The signed-in Console manages its own WorkOS bearer session; never copy that session token or forward it to an integration. ## 1. Create or open a company in the Console Sign in, select an organization, then [generate a company](https://console.fictix.ai/generate) or [open a project](https://console.fictix.ai/projects). When it is ready, open its **Connect to your builder** panel. That panel is the source of truth for the company slug, API origin, and the current project API key. If you need a key, use [Company settings](https://console.fictix.ai/settings/company) → **Company API keys**. Copy the full key only once and save it in your server-side secret manager. ## 2. Set server-side environment variables Use values from the Console's Connect panel. A production configuration looks like this: ```bash export FICTIX_API_ORIGIN="https://api.fictix.ai" export FICTIX_PROJECT_SLUG="replace_with_console_slug" export FICTIX_PROJECT_API_KEY="replace_with_console_key" ``` bash For staging, use `https://api-staging.fictix.ai` and a staging project/key from the Console. ![](phosphor/regular/warning) Never mix a production key with a staging origin or vice versa. ## 3. Confirm the company is reachable GET`/api/v1/orgs/{slug}/info` ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/info" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` bash The response identifies the generated company and includes record-count metadata. A `401` or `403` usually means the key is missing, revoked, or belongs to a different project. Rotate or issue a replacement key in the Console rather than sharing a colleague's key. ## 4. Read company data GET`/api/v1/orgs/{slug}/data/customers` Fetch a small page first: ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/customers?limit=10" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` bash Use a bounded page size and continue until the returned count is satisfied. The same project key can read normalized data, export ledgers, and call supported native-shaped service routes. ## 5. Export a ledger GET`/api/v1/orgs/{slug}/export/invoices` ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/export/invoices?format=csv" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -o invoices.csv ``` bash For a full runtime CSV pack, use the export-pack route documented in [Runtime And Native Routes](/api-reference/runtime-native-routes). For a portable partner loader, use the normalized `/api/v1/demo-jobs` entity exports only when your account has approved control-plane access. ## 6. Discover what Fictix supports Catalog endpoints are public discovery endpoints; they contain no tenant data and do not require a project key: GET`/api/v1/catalog/entities` GET`/api/v1/catalog/services` GET`/api/v1/catalog/connector-capabilities` ```bash curl -sS "$FICTIX_API_ORIGIN/api/v1/catalog/entities" | jq curl -sS "$FICTIX_API_ORIGIN/api/v1/catalog/services" | jq curl -sS "$FICTIX_API_ORIGIN/api/v1/catalog/connector-capabilities" | jq ``` bash Read the catalog before assuming a particular native service/object combination is available. See [Capability Matrix](/api-reference/capability-matrix). ## Minimal server-side loader This Python example follows the count and offset fields in the entity-page response and collects every page. Install its dependency first: ```sh python -m pip install requests ``` sh ```python import os import requests ORIGIN = os.environ["FICTIX_API_ORIGIN"].rstrip("/") SLUG = os.environ["FICTIX_PROJECT_SLUG"] HEADERS = {"X-API-Key": os.environ["FICTIX_PROJECT_API_KEY"]} def fetch_customers(page_size: int = 100) -> list[dict]: url = f"{ORIGIN}/api/v1/orgs/{SLUG}/data/customers" rows = [] seen_ids = set() offset = 0 total = None while total is None or offset < total: response = requests.get( url, headers=HEADERS, params={"limit": page_size, "offset": offset}, timeout=30, ) response.raise_for_status() page = response.json() data = page.get("data") count = page.get("count") if not isinstance(data, list) or type(count) is not int or count < 0: raise ValueError("Expected an entity page with data and a non-negative count") if "offset" in page and page["offset"] != offset: raise ValueError(f"Requested offset {offset}, received {page['offset']}") if len(data) > page_size: raise ValueError(f"Received {len(data)} rows for limit {page_size}") if total is not None and count != total: raise ValueError(f"Entity count changed during pagination: {total} to {count}") total = count for row in data: record_id = row.get("id") if isinstance(row, dict) else None if record_id is None or not str(record_id).strip(): raise ValueError(f"Record at offset {offset} has no id") record_id = str(record_id) if record_id in seen_ids: raise ValueError(f"Duplicate record id across pages: {record_id}") seen_ids.add(record_id) rows.append(row) if not data and offset < total: raise ValueError(f"Pagination stopped at {offset} before count {total}") offset += len(data) if len(rows) != total: raise ValueError(f"Fetched {len(rows)} rows, but count is {total}") return rows customers = fetch_customers() ``` python For a complete runnable Node.js 24 server-side app with a local dashboard and a read-only verifier, download the [customer integration example ZIP](/downloads/fictix-customer-lab.zip). It includes a placeholder `.env.example` and no credentials. Check the [archive manifest](/downloads/fictix-customer-lab.manifest.json) for the included files and their SHA-256 hashes. Its optional native QBO write is an explicitly gated experiment: Fictix's public docs do not define that vendor request body, and connector fidelity and plan entitlement still apply. ![](phosphor/regular/warning) Keep the key in your backend or worker environment. Do not run this request from browser code with the full key exposed. ## Programmatic company creation POST`/api/v1/demo-jobs` ![](phosphor/regular/warning) For external callers, `POST /api/v1/demo-jobs` remains the normalized control-plane contract for approved server-to-server integrations. It requires the Fictix-supplied `FICTIX_ADMIN_API_KEY` / `X-Admin-Key` plus an `Idempotency-Key`. The signed-in Console uses its managed WorkOS bearer session instead; it is not the first step for a Console customer, and its session token must never be copied. Never use email/password auth. See [Demo Jobs](/api-reference/demo-jobs) when your Fictix account has that access. ## Next steps * [Evaluate Your App With Planted Truth](/guides/evaluate-your-app) * [Start In The Fictix Console](/get-started/console-onboarding) * [Runtime And Native Routes](/api-reference/runtime-native-routes) * [Load Fictix Data Into Your App](/guides/load-into-your-app) * [Errors, Limits, And Idempotency](/api-reference/errors-limits-idempotency) # Company blueprints Fictix does not treat a generated company as a random pile of rows. It builds a company blueprint that drives the shape of the synthetic business. Console customers choose and review this shape in the [Fictix Console](https://console.fictix.ai). ![](phosphor/regular/warning) The request examples below are for approved server-to-server control-plane integrations, not a reason to expose a management credential in a frontend. The blueprint is how Fictix decides: * what kind of business this is * how many customers should exist * how revenue relates to headcount * which systems should have data * how much data can be generated safely * whether the classification needs human review ## Sources of company shape Fictix can build a blueprint from: * explicit request fields * domain enrichment * Apollo identifiers * industry and company-size hints * archetype overrides * safe defaults when little information is provided Explicit fields win over inferred fields. ## Important request fields ```json { "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"] } } ``` json ## `company_dna` and `generation_company_dna` Fictix separates actual business facts from safe generation scale. ### `company_dna` Represents the real or requested company. Examples: * actual headcount * actual annual revenue * actual customer accounts * business family * operating profile ### `generation_company_dna` Represents the scaled shape Fictix will generate. This may be smaller than the real company. For example, a company with 1,000,000 real customer accounts might generate a representative weighted sample instead of one million customer records. Use `generation_company_dna` for loader sizing and expected volume. Use `company_dna` for UI copy and business context. ## Classification review Generated company responses can include: ```json { "company": { "classification": { "business_family": "commerce", "review_required": false } } } ``` json ![](phosphor/regular/info) If `review_required` is true, a product UI should ask the user to confirm or override the classification before presenting the dataset as high-confidence. ## Selected services `selected_services` tells Fictix which systems matter for the generated company. Examples: ```json ["qbo", "stripe", "gusto", "hubspot", "asana"] ``` json Selected services affect: * visible connector/system record counts * native-shaped runtime records * export pack relevance * scenario and eval usefulness They do not mean Fictix connects to those real systems. Fictix creates synthetic or native-shaped data that resembles those systems. ## Healthcare / ASC profile Fictix's first healthcare vertical is the **ambulatory surgery center (ASC)** profile. Choose it in the Console, or set `company.industry` to `asc_surgical` in an approved server-to-server create request. The profile adds operating rooms, surgical cases, clinical incidents, and dictated safety reports to the otherwise standard synthetic company. Use the industry field for this profile—not `company.archetype`. The generated records are synthetic and contain no real patient data. See [Healthcare / ASC Data](/concepts/clinical-asc) for entity schemas, safety boundaries, and evaluation guidance. ## Safe scaling Fictix enforces generation budgets. When a request is too large, the API returns an actionable `422` with: * estimated weighted load * estimated records * current budget * quality preset * connector count * suggested safe employees or months of history To reduce load: * lower `generation.employees` * lower `generation.months_of_history` * reduce `selected_services` * choose `clean` or `realistic` instead of `stress_test` * lower customer density in the company shape ## Naming Project names are unique within an organization. If the same name is requested twice, Fictix may append a suffix such as: ```text Local SaaS TestCo (2) ``` text Store the returned job/company name rather than assuming it exactly matches the request. # One key for multiple companies An organization API key lets your server access every current and future company in one Fictix organization. The same key can access 100 companies; each request selects one company by its runtime slug. ![](phosphor/regular/info) Organization keys are available in [staging](https://staging.fictix.ai). Use `https://api-staging.fictix.ai` for the examples on this page. Production continues to support company keys until this feature reaches production. Account limits still apply to company creation and usage. ## Create the key 1. Sign in to the staging Console and open the organization that owns your companies. 2. Open its **Settings**, then **Organization API keys**. You must be an organization admin or owner. 3. Choose a name, access level, and expiry, then select **Create organization key**. 4. Copy the full key while it is displayed and save it in your server's secret store. The full secret is displayed only when a key is created or rotated. Key lists show a masked prefix, status, scopes, usage, and expiry. | Access setting | Scopes | Use | | --------------- | -------------------------- | ----------------------------------------- | | Read only | `read` | Read company data and list companies | | Read and modify | `read`, `write` | Read and modify supported runtime records | | Full runtime | `read`, `write`, `execute` | Also call the execute transport | Runtime operations retain their existing scope requirements. An organization key does not grant company creation, membership, billing, key-management, or other actions that require a signed-in Console session. Ground-truth access requires the separate, explicit `ground_truth` scope; **Full runtime** does not include it. ## List available companies Keep these values on your server: ```bash export FICTIX_API_ORIGIN="https://api-staging.fictix.ai" export FICTIX_ORGANIZATION_ID="replace_with_organization_id" export FICTIX_ORGANIZATION_API_KEY="replace_with_organization_key" curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/organizations/$FICTIX_ORGANIZATION_ID/companies?limit=100&offset=0" \ -H "X-API-Key: $FICTIX_ORGANIZATION_API_KEY" ``` bash The organization ID appears in the directory request shown below the key controls. A response has this shape; the identifiers below are fictional: ```json { "organization_id": "example_org", "companies": [ {"id": "company_a", "name": "Fictional Estate Software", "slug": "estate-software-demo", "status": "ready", "org_id": "example_org"}, {"id": "company_b", "name": "Fictional Advisory Services", "slug": "advisory-services-demo", "status": "ready", "org_id": "example_org"} ], "count": 2, "total": 2, "limit": 100, "offset": 0 } ``` json For this directory, `count` is the number returned on the current page and `total` is the number in the organization. Continue while `offset + count < total`. The maximum `limit` is 1,000. ## Select a company on each request Change the slug and keep the same key: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/estate-software-demo/data/customers?limit=10" \ -H "X-API-Key: $FICTIX_ORGANIZATION_API_KEY" curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/advisory-services-demo/data/customers?limit=10" \ -H "X-API-Key: $FICTIX_ORGANIZATION_API_KEY" ``` bash `/organizations/{org_id}` identifies your Fictix organization. The older `/orgs/{slug}` runtime path identifies a generated **company**. For execute request bodies, the legacy `org_id` field is the selected company's `id`; it is not the organization ID from the directory URL. Company keys continue to work for their own company. An organization key cannot access a company owned by another organization. Moving a company out removes that organization's key access; moving one in allows its new organization's keys to access it. ## Rotate and revoke **Rotate** creates a replacement and immediately revokes the previous key, while preserving its scopes and expiry. Save the replacement and update your server. If you need an overlap during rollout, create a second key, update your integrations, and then revoke the first key. **Revoke** stops that key across all companies in the organization. Expired and revoked keys return `401`. A valid key selecting a company outside its organization returns `404` (`Project not found`). If you receive `404`, check the company slug and the organization that owns it. Keep the key out of frontend JavaScript, public environment variables, and source control. The [Company API reference](/api/company) documents runtime requests; routes requiring Console bearer authentication remain separate. # Connect your software to Fictix Point your server's provider client at a generated company's Fictix URL, add its runtime API key, and send a supported provider request. Records and writes belong to that synthetic company. Fictix does not log in to your vendor account, send real messages, run ads, or move money through a bank. ## 1. Choose the environment and company Public catalog snapshots observed on October 28, 2026 report 60 entries in both staging and production: 59 marked available/selectable and one FCI execute preview that is not selectable. All 60 report approximate fidelity. These are catalog reports, not proof that provider requests work or strict upstream API parity. The published first-request templates were tested in staging only. Use the [first-request directory](/api-reference/provider-first-requests) to check catalog status for each provider; [workflow coverage](/api-reference/provider-workflows) lists the supported staging operations and limits. To evaluate the expanded integrations, sign in at [staging.fictix.ai](https://staging.fictix.ai), create or select a company, and choose the integrations your software needs. Onboarding, the connector picker, and the add-integration menu use the deployed catalog. Your organization's plan and the company's selected services still determine access. Open the company's **API** page to select a provider and copy its API URL, key, and first request. The **Connect to your builder** panel also provides setup details. Keep the key and API origin in the same environment. ## 2. Choose a runtime key Use a **Company API key** for one company. For one key across your current and future companies, staging organization admins can use an [organization API key](/get-started/organization-api-keys). Both authenticate with `X-API-Key`; neither is a vendor OAuth token. | Operation | Required runtime-key scopes | | ------------------------------------------------------ | --------------------------- | | Native read, including a supported read sent with POST | `read` | | Native create, update, or delete | `write` | | Execute a read capability, including FCI | `read`, `execute` | | Execute a capability that writes records | `write`, `execute` | Choose the scopes needed by your app when issuing the key. The Console's **Full runtime** organization-key setting includes `read`, `write`, and `execute`. It does not include the separate `ground_truth` scope. Keys never override plan entitlements or grant management access. Keep credentials in your server's secret store. A browser app calls your own server, which attaches the key when calling Fictix. Use the existing [browser-builder proxy guidance](/resources/agent-integration) for browser apps. ## 3. Set the base URL Copy the slug and key from the selected staging company: ```bash export FICTIX_API_ORIGIN="https://api-staging.fictix.ai" export FICTIX_PROJECT_SLUG="replace_with_company_slug" export FICTIX_PROJECT_API_KEY="replace_with_company_key" ``` bash These examples use a company key. For an organization key, send `$FICTIX_ORGANIZATION_API_KEY` in the same header and select a company by its slug. The organization-key guide shows how to list your company IDs and slugs. | URL | Purpose | | --------------------------------------- | -------------------------------- | | `/api/v1/orgs/{slug}` | Company runtime base | | `/api/v1/orgs/{slug}/native/{provider}` | Native provider base | | `/api/v1/orgs/{slug}/execute` | Execute transport, including FCI | For example, Stripe's provider base is `https://api-staging.fictix.ai/api/v1/orgs/{slug}/native/stripe`. Append `v1/customers` for its first read: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/stripe/v1/customers" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` bash If an SDK adds its own `/v1`, `/api`, or service prefix, configure its server URL so the resulting request matches the full path in the [first-request directory](/api-reference/provider-first-requests). Configure its transport to send the Fictix header. An SDK that cannot change its host or headers needs a custom transport or a direct HTTP client. Vendor login flows and SDK defaults are not Fictix authentication. ## 4. Check discovery and the response The catalog is public. Query the environment your server actually uses: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/catalog/connector-capabilities" ``` bash Find your provider by `id`. Check `status`, `ui_selectable`, `native_supported`, `contract_surfaces`, and `limitations`. FCI has an execute surface and no native surface. A missing provider is unavailable in that environment. Relay Financial has no implemented direct API contract and is not in the available catalog. Provider responses retain their own envelopes, pagination, and content types: * REST requests usually use JSON, but their list fields differ by provider. * Linear and Monday use GraphQL over POST. Inspect the GraphQL `errors` field. * Sage Intacct uses XML. Inspect every returned operation status. * Expensify uses a URL-encoded `requestJobDescription` form field. Check `responseCode` as well as the HTTP status. * Execute responses can use HTTP 200 with `status: "error"`. Require `status: "success"` before consuming `outputs`. Use the request bodies and media types in the first-request directory. A POST read does not become a write merely because of its HTTP method. ## 5. Use FCI through execute Read the company's `id` from `GET /api/v1/orgs/{slug}/info`, the API page's generated request, or the organization company directory. Put that **company project ID** in the legacy `org_id` body field. The workspace organization ID and the URL's company slug are different identifiers. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/execute" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"org_id":"replace_with_company_project_id","user_id":"your-integration","turn_id":"replace_with_unique_request_id","capability_key":"fci.cash","args":{}}' ``` bash This key needs both `read` and `execute`. FCI returns derived financial summaries and retains preview status. Confirm its capability is available for your company; it is not selectable in the standard connector picker. There is no `/native/fci` first request. ## 6. Make writes reproducible Native writes should include a fresh `Idempotency-Key` per operation. Reuse it only for a retry with the same request and payload. Execute requests use a unique `turn_id` per operation; preserve it on retries. Follow the provider's documented exception where its own idempotency field is supported. Read the resulting object back to verify persistence. Use each provider's pagination contract; canonical `limit`/`offset` is not a universal native API pagination scheme. Unsupported operations return errors rather than extending the vendor contract automatically. If a call returns `401`, check the environment, expiry, and revocation state of the key. A `402` with `feature_not_entitled` means the organization lacks the required plan feature; native integration APIs require `native.genome`, available on Developer or higher. Creating a new key does not change plan access. Canonical REST data can still be available on your existing plan. For `403`, check the key's company or organization and required scopes. For `400`, `404`, or `422`, compare the exact method, path, body, and content type with the documented workflow. The [Company API reference](/api/company) describes runtime routing and schemas. The provider guides supply the concrete vendor paths hidden behind its generic native route. Use the [Partner API](/api/partner) only for an approved external management integration. # Entities, counts, and lineage Fictix generates canonical business entities and can also expose native-shaped records for selected services. Canonical entities are the stable integration layer. Native-shaped records are for testing product integrations that expect a provider-shaped API. ![](phosphor/regular/info) For a Console-created company, obtain the project key and runtime base URL from the [Fictix Console](https://console.fictix.ai) before reading entities. Partner `/demo-jobs` page examples are documented separately in [Entities and Exports](/api-reference/entities-and-exports). Runtime browsing uses `/api/v1/orgs/{slug}/data/{entity}`. ## Canonical entity families Common canonical entities: * `accounts` * `bank_accounts` * `bank_transactions` * `bills` * `customers` * `customer_payments` * `credit_memos` * `deals` * `employees` * `invoices` * `items` * `journal_entries` * `payroll_runs` * `tasks` * `vendor_payments` * `vendors` Alias behavior: * `payments` resolves to `customer_payments` * `bill_payments` resolves to `vendor_payments` Prefer canonical names in new integrations. ### ASC clinical extensions Companies created with `company.industry: "asc_surgical"` also expose: * `operating_rooms` * `surgical_cases` * `clinical_incidents` * `dictation_scripts` These are additive entity families: they are absent from non-ASC companies. `dictation_scripts.labels` is a synthetic ground-truth extraction answer key, not real clinical documentation. See [Healthcare / ASC Data](/concepts/clinical-asc). ## Counts Fictix exposes counts in multiple places: * `demo_job.dataset_summary.record_counts` * `GET /api/v1/demo-jobs/{job_id}/status` * entity page response `count` * catalog entity metadata Use counts to: * decide which loaders to run * show UI totals * verify exports * detect partial loads ## Runtime pages Project-key runtime entity pages use this response shape: ```json { "data": [], "count": 0, "total_count": 0, "limit": 100, "offset": 0, "next_cursor": null, "service": null } ``` json This example shows an empty page. Runtime responses follow the `RuntimeEntityPage` schema in the public [`/openapi.json`](https://api.fictix.ai/openapi.json). `count` is the total for the requested canonical entity; `data` contains the current page. Continue while **offset + data.length < count**. Advance the next offset by the number of rows actually returned, not by the requested page limit. Stop when **offset + data.length >= count**; treat an empty page before the reported count as a partial load that needs investigation. ## Exports Entity exports support: ```text format=json format=csv ``` text Use JSON when preserving nested structures matters. Use CSV when the host product has spreadsheet or batch-import workflows. ## Lineage Responses that derive from generated data include lineage metadata where available. Lineage is how an integration can answer: * which generated company produced this record? * which seed/config generated it? * which validation certificate applies? * does this data come from a scenario-injected run? When loading Fictix data into another product, store lineage alongside imported records when possible. Recommended host-product columns: * `fictix_job_id` * `fictix_entity` * `fictix_record_id` * `fictix_seed` * `fictix_dataset_hash` * `fictix_loaded_at` ## Referential integrity Fictix aims to keep core references coherent: * invoices reference customers * bills reference vendors * customer payments reference invoices * applied credit memos link to invoices and match the invoice customer and currency * vendor payments reference bills * bank transactions reference bank accounts * invoice and bill lines reference items * payroll runs reference employees * journal entries balance debit and credit totals * surgical cases reference operating rooms when the ASC pack is selected * clinical incidents can reference surgical cases and their dictation scripts Treat missing references as a data quality defect and inspect `GET /api/v1/demo-jobs/{job_id}/quality`. Runtime-only integrations should record the project slug and the Console generation timestamp with their own loader result, then use the Console or an approved control-plane flow when they need the full certificate. ## Synthetic safety Generated emails and domains should use reserved namespaces such as: * `.example` * `.test` * `.invalid` ![](phosphor/regular/warning) This prevents synthetic data from appearing to belong to real companies or real people. # Build a demo tenant This guide shows how to turn a user request into a demo tenant in your product. For a standard Fictix customer, create the company in the [Fictix Console](https://console.fictix.ai) and connect it with a project runtime key. ![](phosphor/regular/warning) The programmatic control-plane flow below is for approved backend-to-backend integrations. Configure the Fictix-supplied `FICTIX_ADMIN_API_KEY` as `X-Admin-Key` server-side. The signed-in Console uses its own managed WorkOS bearer session for Console actions; that session is not a customer credential to copy or forward. Never use an email/password login, copy a browser session token, or put the admin key in frontend code. ## Goal Given a user selection like: ```text Create a mid-market logistics company with QBO, Gusto, HubSpot, and Asana data. ``` text Your system should: 1. create a Fictix demo job 2. wait for readiness 3. fetch entity data 4. map it into your product schema 5. store lineage 6. show the user a ready demo tenant ## Recommended architecture ```text Browser -> your backend -> Fictix approved server-to-server demo-jobs API -> your loader workers -> your database -> browser sees ready tenant ``` text Do not call Fictix with management credentials directly from the browser. ## Step 1: Build the create request Map your UI form into the Fictix create payload: ```json { "company": { "name": "Acme Logistics", "domain": "acme-logistics.example", "industry": "Logistics", "employee_count": 120, "annual_revenue": 24000000, "customer_accounts": 1500, "selected_services": ["qbo", "gusto", "hubspot", "asana"] }, "generation": { "employees": 120, "months_of_history": 24, "quality_preset": "realistic" }, "simulation": { "seed": 42 } } ``` json Use `.example` domains for clearly synthetic companies. ### ASC healthcare tenant For a synthetic ambulatory surgery center, use `company.industry` rather than `company.archetype`: ```json { "company": { "name": "Lakeside Surgery Center", "domain": "lakeside-surgery.example", "industry": "asc_surgical" } } ``` json The resulting company adds synthetic clinical entities. It contains no real patient data; see [Healthcare / ASC Data](/concepts/clinical-asc) before loading or evaluating it. ## Step 2: Create job with idempotency Generate an idempotency key from your internal request id: ```text tenant-create-{tenant_request_id} ``` text Then call: POST`/api/v1/demo-jobs` ```http POST /api/v1/demo-jobs ``` http Store: * Fictix job id * original request * returned company metadata * status * links * runtime ids ## Step 3: Wait for readiness Poll: GET`/api/v1/demo-jobs/{job_id}/status` ```http GET /api/v1/demo-jobs/{job_id}/status ``` http Treat `status=ready` as generation complete, then inspect quality: GET`/api/v1/demo-jobs/{job_id}/quality` ```http GET /api/v1/demo-jobs/{job_id}/quality ``` http ![](phosphor/regular/warning) Do not show a polished demo tenant if quality is unacceptable for your use case. ## Step 4: Load core records Recommended first load: 1. `accounts` 2. `bank_accounts` 3. `items` 4. `customers` 5. `vendors` 6. `employees` 7. `invoices` 8. `credit_memos` 9. `bills` 10. `customer_payments` 11. `vendor_payments` 12. `bank_transactions` 13. `journal_entries` 14. `deals` 15. `tasks` 16. `payroll_runs` For ASC companies, add `operating_rooms`, `surgical_cases`, `clinical_incidents`, and `dictation_scripts` after the standard entities. Fetch pages: GET`/api/v1/demo-jobs/{job_id}/entities/{entity}?limit=500&offset=0` ```http GET /api/v1/demo-jobs/{job_id}/entities/{entity}?limit=500&offset=0 ``` http ## Step 5: Store lineage For each imported record, store: * `fictix_job_id` * `fictix_entity` * `fictix_record_id` * `fictix_loaded_at` * `fictix_dataset_hash`, if available Lineage makes support, debugging, and replays possible. ## Step 6: Expose tenant state to users Show: * company name * generated systems * loaded record counts * generation quality * last loaded timestamp * any warnings Avoid showing raw Fictix credentials in UI. ## Failure handling | Failure | User-facing behavior | Backend action | | -------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | Auth failure | Route a normal customer to the Console; approved integrations should contact Fictix support | Verify exactly one approved management credential; do not retry with a project key, email/password, or browser session token | | Generation budget exceeded | Ask user to reduce scale | Lower employees/months/services | | Bundle too large | Continue loading by entity | Use entity pages | | Quality failed | Show warning or block launch | Inspect quality endpoint | | Loader partial failure | Show partial state | Resume from last entity/page | ## Production checklist * idempotency key is stable per user request * no management or project key in browser * generated data is marked synthetic in your product * loader is resumable * lineage is stored * quality endpoint is checked * counts are reconciled after load * runtime API key is encrypted or not stored # Evaluate your app with planted truth Create a company in the [Fictix Console](https://console.fictix.ai), plant a supported issue, run your detector against the company records, and submit its findings for a score. The company-key REST flow below uses the company you already created in the Console. ## 1. Prepare the company and keys Follow the [Quickstart](/get-started/quickstart) to create a synthetic test company and get its slug, API origin, and company key. Use the matching production or staging origin throughout. In **Settings → Company → Company API keys**, choose **Read and modify** for the server-side evaluation harness. Planting scenarios and creating test matrices/runs require `write`; reading company records requires `read`. Give the application being evaluated a separate **Read only** key. Keep both keys on their respective servers, using `FICTIX_PROJECT_API_KEY` in each process. Neither process needs a copied Console session or an admin key. The commands below use the harness key and the three environment variables from the quickstart. ## 2. Choose and plant one supported case Read the library before choosing a case: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/scenarios" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` bash Check the template's `entity_types_affected` and parameter definitions. `duplicate-expense-fraud` plants duplicate **vendor bills**. The current library does not include a duplicate-customer-invoice template. Initialize simulation state, then pause company activity while you evaluate. Starting an existing simulation returns its current state; it does not create another company. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/simulation/start" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"seed":42,"dials":{"trickle_rate":"paused"}}' curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/simulation/pause" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` bash Plant two duplicate bills with no amount variation: ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/simulation/scenarios" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"scenarios":[{"id":"duplicate-expense-fraud","params":{"count":2,"amount_variance_pct":0}}]}' ``` bash Inject once. If the connection fails before you receive a response, check the company and scenario state before repeating a write. Scenarios consume the company's scenario quota. ## Runnable duplicate-bill example Download the [customer integration example](/downloads/fictix-customer-lab.zip), configure the quickstart's three server-side variables, and use its exact bill-repeat detector: ```bash npm run evaluate:bills npm run evaluate:bills -- --submit ``` bash The first command reads bills and prints findings. The second also creates a matrix and submits one test run using your **Read and modify** harness key. It prints the actual score and run ID. It uses only company-key REST routes, without fetching the planted-truth manifest. The example compares exact amounts, matching the zero-variance case above. You can use this command instead of manually performing steps 3–5 below. ## 3. Run your application against the records Use the application's read-only key to page `/data/bills`, or load an export into your own application. Preserve each record's Fictix `id`. Your detector should inspect business fields such as vendor, bill number, currency, amount, and dates. Keep planted-truth manifests outside the detector. Collect the actual record IDs it identifies: ```json { "detected": [ { "entity_type": "bill", "entity_id": "replace_with_an_id_your_detector_found", "finding": "Repeated bill number and amount for the same vendor" } ] } ``` json Save this as `detector-findings.json`. Use real IDs from your detector; the placeholder above is not a detection. An empty `detected` array is a valid no-findings result. ## 4. Create a test matrix With the harness key, create a matrix for the case you planted: ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/test-matrices" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"name":"Duplicate bill evaluation","scenarios":[{"scenario_id":"duplicate-expense-fraud","severity":"high","required":true}]}' \ -o matrix.json ``` bash The response contains `matrix_id`. Build a submission using that returned ID: ```bash jq --arg matrix_id "$(jq -r '.matrix_id' matrix.json)" \ '{matrix_id:$matrix_id,detected:.detected}' detector-findings.json > test-run.json ``` bash ## 5. Submit findings and read the score ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/test-runs" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @test-run.json \ -o result.json ``` bash Read `overall.overall_recall`, `overall.overall_precision`, `overall.overall_f1`, and `overall.matrix_passes` for the aggregate result. `results` contains scenario-level recall, precision, missed needles, and false positives. Retain `run_id`, `manifest_hash`, and your application/model version so you can compare later runs against the same evidence. Test runs consume the company's test-run quota. Retrieve a previous run with `GET /api/v1/orgs/{slug}/test-runs/{run_id}`. To list a matrix's history, use `GET /api/v1/orgs/{slug}/test-runs?matrix_id={matrix_id}`; the matrix ID is required. Inspect that history before retrying an uncertain submission. A no-findings submission should record missed planted issues. HTTP 401/403, invalid JSON, or a failed detector request are integration failures rather than successful evaluation results. ## Use Console Test Lab with your detector endpoint You can also create a scenario in **Scenarios**, then open **Test Lab**, enter your detector URL, and choose **Run tests**. The Console handles the matrix and scoring requests with your signed-in session. The Console sends an HTTP POST from your browser to the detector: ```json { "mode": "fictix-test-run", "project_slug": "your-company-slug", "project_id": "your-company-project-id", "scenario_ids": ["duplicate-expense-fraud"] } ``` json Return HTTP 200 with a JSON `detected` array in the format shown above. The endpoint must respond within 30 seconds. HTTPS is supported; local development also permits HTTP on `localhost` or `127.0.0.1`. For localhost tests in Chrome, allow the Console site's [local network permission](https://developer.chrome.com/docs/modern-web-guidance/guides/security/local-network-access) when prompted. If you blocked it, enable local network or loopback access in the site's browser permissions and retry. A blocked request can time out before your detector receives it. Allow the calling Console origin through CORS: `https://console.fictix.ai` for production or `https://staging.fictix.ai` for staging. Handle the OPTIONS preflight and allow POST with the `Content-Type` header. The Console sends no detector authorization header and never forwards your Fictix company key. Your detector server supplies its own read-only company key when reading records. ## Approved demo-job integrations Fictix-approved management integrations can use the separate [demo-job API](/api-reference/demo-jobs) and its [test-matrix/run routes](/api-reference/test-matrices-runs). Those routes use Fictix-supplied management credentials. Console customers can use the company-key workflow above. # Catalog Catalog endpoints are read-only discovery endpoints. Use them before creating a job to learn what Fictix can generate and how returned records will look. Base path: ```text /api/v1/catalog ``` text For how catalog results map to project runtime calls, see the [Capability Matrix](/api-reference/capability-matrix). ![](phosphor/regular/info) Authentication: none. Catalog endpoints are public discovery endpoints and contain no organization, project, credential, or generated tenant data. ## List archetypes GET`/api/v1/catalog/archetypes` ```http GET /api/v1/catalog/archetypes ``` http Returns supported company archetypes and key generation defaults. Response shape: ```json { "object": "catalog_collection", "contract_version": "2026-07-17", "collection": "archetypes", "count": 3, "items": [ { "id": "saas_b2b", "name": "Saas B2B", "customers_per_employee": 3.0, "payment_rate": 0.92, "monthly_churn_rate": 0.02 } ] } ``` json Use this to populate archetype pickers or to validate user input before job creation. ## List quality presets GET`/api/v1/catalog/presets` ```http GET /api/v1/catalog/presets ``` http Returns: * `clean` * `realistic` * `messy` * `stress_test` Response item: ```json { "id": "realistic", "name": "Realistic", "description": "Minor real-world defects without breaking core workflows.", "defect_rate": 0.05 } ``` json ## List entities GET`/api/v1/catalog/entities` ```http GET /api/v1/catalog/entities ``` http Returns the public entity families available through demo-job entity APIs. Use this endpoint to: * build entity browsers * choose loader order * validate export requests * show users what data is available ## List services GET`/api/v1/catalog/services` ```http GET /api/v1/catalog/services ``` http Returns supported connector/service families and visible record types. Use this endpoint when presenting selected services in a create-job UI. ## List connector capabilities GET`/api/v1/catalog/connector-capabilities` ```http GET /api/v1/catalog/connector-capabilities ``` http Query params: | Name | Type | Default | Description | | --------------------- | ------- | ------- | ---------------------------------------- | | `include_unsupported` | boolean | `false` | Include unsupported connector manifests. | Response includes: * total count * selectable count * unsupported count * capability metadata per connector ## List export packs GET`/api/v1/catalog/export-packs` ```http GET /api/v1/catalog/export-packs ``` http Returns shipped file-oriented export packs such as QBO, HubSpot, Asana, and captured provider packs. Use this when the host product wants a file import path instead of a JSON API path. ## Get service object schema GET`/api/v1/catalog/services/{service_id}/objects/{object_type}/schema` ```http GET /api/v1/catalog/services/{service_id}/objects/{object_type}/schema ``` http Examples: ```text GET /api/v1/catalog/services/qbo/objects/invoice/schema GET /api/v1/catalog/services/stripe/objects/customer/schema ``` text Returns display columns for generic and native-shaped object views. ## Get export pack schema GET`/api/v1/catalog/export-packs/{target}/schema` ```http GET /api/v1/catalog/export-packs/qbo/schema GET /api/v1/catalog/export-packs/{target}/schema ``` http Use schema endpoints to: * display available CSV files * validate import tooling * generate docs for export bundles * compare expected files against downloaded artifacts # Entities and exports Entity endpoints return canonical Fictix data from one demo job. For a self-service Console-created company, start with the project runtime routes in [Runtime And Native Routes](/api-reference/runtime-native-routes). The normalized `/demo-jobs` entity surface is for approved control-plane integrations. ## Get a full bundle GET`/api/v1/demo-jobs/{job_ref}/bundle` ```http GET /api/v1/demo-jobs/{job_ref}/bundle ``` http Query params: | Name | Type | Description | | --------------- | --------------- | ------------------------------------------------------ | | `entity` | repeated string | Include only selected entity families. | | `include_empty` | boolean | Include empty entity families when resolving defaults. | | `max_records` | integer | Optional cap, bounded by server maximum. | Example: ```bash curl "$FICTIX_API_ORIGIN/api/v1/demo-jobs/$JOB_ID/bundle?entity=customers&entity=invoices" \ -H "X-Admin-Key: $FICTIX_ADMIN_API_KEY" ``` bash Response: ```json { "object": "demo_bundle", "contract_version": "2026-07-17", "job": {}, "lineage": {}, "included_entities": ["customers", "invoices"], "total_records": 1200, "record_limit_applied": 25000, "entities": { "customers": [], "invoices": [] } } ``` json ![](phosphor/regular/info) If the bundle exceeds the cap, Fictix returns `413`. Use entity paging or export instead. ## Get entity page GET`/api/v1/demo-jobs/{job_ref}/entities/{entity}` ```http GET /api/v1/demo-jobs/{job_ref}/entities/{entity} ``` http Query params: | Name | Type | Default | Description | | -------- | ------- | ------- | ------------------------ | | `limit` | integer | `100` | Page size. | | `offset` | integer | `0` | Offset into entity list. | Example: ```bash curl "$FICTIX_API_ORIGIN/api/v1/demo-jobs/$JOB_ID/entities/customers?limit=100&offset=0" \ -H "X-Admin-Key: $FICTIX_ADMIN_API_KEY" ``` bash Response: ```json { "object": "demo_entity_page", "contract_version": "2026-07-17", "job_id": "8f3a2d11", "lineage": {}, "entity": "customers", "count": 1200, "limit": 100, "offset": 0, "data": [] } ``` json ## Export entity GET`/api/v1/demo-jobs/{job_ref}/entities/{entity}/export` ```http GET /api/v1/demo-jobs/{job_ref}/entities/{entity}/export?format=json GET /api/v1/demo-jobs/{job_ref}/entities/{entity}/export?format=csv ``` http JSON response: ```json { "object": "demo_entity_export", "contract_version": "2026-07-17", "job_id": "8f3a2d11", "lineage": {}, "entity": "invoices", "format": "json", "count": 200, "data": [] } ``` json CSV response returns `text/csv` content. ## Supported entities Preferred canonical entity names: | Entity | Description | | ------------------- | ------------------------------------------------------- | | `accounts` | Chart of accounts. | | `bank_accounts` | Bank account records. | | `bank_transactions` | Bank ledger activity. | | `bills` | Accounts payable bills. | | `customers` | Customer master records. | | `customer_payments` | Customer receipts. | | `credit_memos` | Applied accounts receivable credits linked to invoices. | | `deals` | CRM deals/opportunities. | | `employees` | Employee records. | | `invoices` | Accounts receivable invoices. | | `items` | Product/service items. | | `journal_entries` | General ledger journal entries. | | `payroll_runs` | Payroll batches and pay-period output. | | `tasks` | Operational task records. | | `vendor_payments` | Vendor/bill payments. | | `vendors` | Vendor master records. | ASC clinical extensions are available only for companies created with `company.industry: "asc_surgical"`: | Entity | Description | | -------------------- | ----------------------------------------------------------------------- | | `operating_rooms` | Operating and procedure rooms. | | `surgical_cases` | Scheduled synthetic surgical cases. | | `clinical_incidents` | Synthetic safety events and near misses with ground-truth fields. | | `dictation_scripts` | Spoken-style incident narrations with synthetic extraction answer keys. | The clinical pack contains no real patient data. For activation, record shape, scenario needles, and evaluation guidance, see [Healthcare / ASC Data](/concepts/clinical-asc). Aliases: | Alias | Canonical entity | | --------------- | ------------------- | | `payments` | `customer_payments` | | `bill_payments` | `vendor_payments` | New integrations should use canonical names to avoid double-counting. ## Loader order For relational loading, use this order: 1. `accounts` 2. `bank_accounts` 3. `items` 4. `customers` 5. `vendors` 6. `employees` 7. `invoices` 8. `credit_memos` 9. `bills` 10. `customer_payments` 11. `vendor_payments` 12. `bank_transactions` 13. `journal_entries` 14. `deals` 15. `tasks` 16. `payroll_runs` For an ASC company, then load `operating_rooms`, `surgical_cases`, `clinical_incidents`, and `dictation_scripts` in that order. This order ensures reference targets exist before dependent records. # Test matrices and runs Test matrices define what an application should detect. Test runs submit what the application actually detected. Fictix scores the submitted findings against planted truth. Base path: ```text /api/v1/demo-jobs/{job_ref} ``` text ## Create test matrix POST`/api/v1/demo-jobs/{job_ref}/test-matrices` ```http POST /api/v1/demo-jobs/{job_ref}/test-matrices ``` http Example body: ```json { "name": "Accounting QA", "scenarios": [ { "scenario_id": "ghost-employee", "severity": "critical", "required": true } ] } ``` json Use a matrix to define the expected detection contract for a product workflow. ## List test matrices GET`/api/v1/demo-jobs/{job_ref}/test-matrices` ```http GET /api/v1/demo-jobs/{job_ref}/test-matrices ``` http ## Get test matrix GET`/api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id}` ```http GET /api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id} ``` http ## Delete test matrix DELETE`/api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id}` ```http DELETE /api/v1/demo-jobs/{job_ref}/test-matrices/{matrix_id} ``` http ## Create test run POST`/api/v1/demo-jobs/{job_ref}/test-runs` ```http POST /api/v1/demo-jobs/{job_ref}/test-runs ``` http Example body: ```json { "matrix_id": "tm_abc123", "detected": [ { "entity_type": "employee", "entity_id": "emp_ghost_001", "finding": "ghost employee" } ] } ``` json Fictix compares submitted findings with the matrix and scenario truth. ## List test runs GET`/api/v1/demo-jobs/{job_ref}/test-runs` ```http GET /api/v1/demo-jobs/{job_ref}/test-runs ``` http ## Get test run GET`/api/v1/demo-jobs/{job_ref}/test-runs/{run_id}` ```http GET /api/v1/demo-jobs/{job_ref}/test-runs/{run_id} ``` http ## Evaluation pattern 1. Create a demo job. 2. Inject or generate scenarios. 3. Fetch the scenario manifest. 4. Load the company into the target application. 5. Run the target application or agent. 6. Convert the target application's findings into Fictix detected findings. 7. Submit a test run. 8. Store the score and verdict. ## Detected finding shape Detected findings should be specific enough to match planted truth: ```json { "entity_type": "vendor", "entity_id": "ven_123", "finding": "duplicate vendor" } ``` json Avoid vague findings such as: ```json { "finding": "something is wrong" } ``` json ![](phosphor/regular/info) Specific findings produce better scoring and better user-facing verdicts. # Response envelopes Fictix returns generated business records, so entity-specific data can vary by company, selected services, and scenario. The partner API keeps the outer envelope stable while allowing the record payload to remain true to the generated company. The live schema is available at: ```text https://api.fictix.ai/openapi.json ``` text Use the schema for mechanical route, request, authentication, and top-level response validation. Use the catalog endpoints to discover the fields and capabilities selected for a particular company. ## Contract version Partner response envelopes include a date-based contract version: ```json { "contract_version": "2026-07-17" } ``` json Treat it as a string, not an integer. Store the version with any imported dataset, export, test run, or evaluation result. ## Demo job ```json { "object": "demo_job", "contract_version": "2026-07-17", "regeneration_required": false, "id": "8f3a2d11", "status": "ready", "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": {}, "dataset_summary": {}, "quality": {} } ``` json | Field | Type | Meaning | | ----------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------- | | `object` | string | Stable resource type: `demo_job`. | | `contract_version` | string | Partner contract version for the response. | | `regeneration_required` | boolean | `true` when this company predates the current accounting contract and must be regenerated before a current-contract import. | | `id` | string | Demo-job/project identifier. | | `status` | string | `created`, `generating`, `ready`, `error`, or `unknown`. | | `links` | object | Relative links to the job's normalized resources. | | `company` | object | Generated company identity and summary. | | `runtime` | object | Runtime project id, slug, and handoff metadata. | | `dataset_summary` | object | Canonical record counts and materialization summary. | | `quality` | object | Validation summary appropriate for fast display. | ![](phosphor/regular/warning) The `credentials.api_key` field can appear immediately after a control-plane create/clone. It is a one-time server secret; do not log or return it to a browser. Console-created companies use the Console's **Company API keys** flow instead. ## Entity page ```json { "object": "demo_entity_page", "contract_version": "2026-07-17", "job_id": "8f3a2d11", "lineage": {}, "entity": "customers", "count": 1200, "limit": 100, "offset": 0, "data": [] } ``` json ![](phosphor/regular/info) `count` is the total for the requested canonical entity; `data` contains the current page. Do not infer end-of-pagination from a short page alone—continue until `offset + data.length >= count`. ## Catalog collection ```json { "object": "catalog_collection", "contract_version": "2026-07-17", "collection": "services", "count": 4, "items": [] } ``` json Catalog endpoints are public and return no tenant data. They are the right source for available entities, connector capabilities, and export-pack schemas. ## Error envelopes Most errors include `detail`. The value may be text or a structured object: ```json { "detail": { "code": "project_dataset_limit_exceeded", "message": "Project dataset exceeds current blob storage safety budget" }, "request_id": "req_example" } ``` json Create-time credit errors (`402`, and some `409`, `422`, and `429` responses) use a separate envelope: ```json { "error": "insufficient_credits", "message": "A valid credit run or sufficient credits are required." } ``` json Write clients that tolerate both envelope families: ```ts type FictixDetailError = { detail: string | { code?: string; message?: string; [key: string]: unknown }; request_id?: string; }; type FictixCreditError = { error: string; message: string; }; type FictixError = FictixDetailError | FictixCreditError; ``` ts ![](phosphor/regular/warning) Capture `request_id`, method, path, and safe job/entity identifiers when escalating a problem. Never include credentials in support logs. ## CSV and JSON exports `format=json` returns a `demo_entity_export` JSON envelope. `format=csv` returns `text/csv` and includes lineage in response headers when available. Choose CSV for file loaders and JSON for API-based transforms; do not expect the CSV endpoint to wrap rows in the JSON envelope. Next: [Entities And Exports](/api-reference/entities-and-exports) and [Errors, Limits, And Idempotency](/api-reference/errors-limits-idempotency). # Webhooks Webhooks let a Fictix runtime notify your application when simulation, data, or test events occur. Use webhooks when your product needs to react to a generated company over time instead of polling event endpoints. Webhook management currently lives on the runtime org surface: ```text /api/v1/orgs/{slug}/webhooks ``` text ![](phosphor/regular/info) Start in the [Fictix Console](https://console.fictix.ai): the company owner can confirm the project, plan entitlement, runtime key, and webhook configuration there. ![](phosphor/regular/warning) Keep subscription creation and test delivery server-side; never expose the project key or any other credential in browser code. The `slug` is returned from the demo job resource at: ```json { "runtime": { "slug": "acme-logistics" } } ``` json ## Entitlement Webhook subscriptions require the `webhooks` entitlement and currently require a plan with the `webhooks` feature enabled (Developer, Pro, or Enterprise in the default catalog). If the project is not entitled, the API returns `403`. ## Authentication Create, delete, and test are Console/org-admin actions. The signed-in Fictix Console manages a WorkOS-backed bearer session, and a direct authenticated WorkOS org administrator may use that session type for these operations: ```http Authorization: Bearer ``` http The Console owns its session; it is not an external integration credential to source, copy, or forward. External server-to-server integrations use the Fictix-supplied `X-Admin-Key` on the demo-job control plane, not a copied or invented management bearer. Listing accepts either an authenticated WorkOS org-user bearer session or the project runtime key issued by the Console: ```http X-API-Key: ``` http ![](phosphor/regular/warning) Keep webhook management server-side. Do not expose the project runtime API key or any other credential in browser code. ## Create a webhook POST`/api/v1/orgs/{slug}/webhooks` ```http POST /api/v1/orgs/{slug}/webhooks Content-Type: application/json Authorization: Bearer ``` http Request: ```json { "url": "https://example.test/fictix/webhooks", "events": ["fictix.test", "invoice.issued"], "secret": "whsec_32_bytes_or_more" } ``` json Fields: | Field | Type | Required | Notes | | -------- | -------------------- | -------- | -------------------------------------------------------------------------------------------------- | | `url` | URL | Yes | HTTPS callback URL. The server validates callback safety before storing it. | | `events` | string array or null | No | `null` or omitted subscribes to all events. A provided list is capped to the first 50 event names. | | `secret` | string or null | No | Shared signing secret. Store a high-entropy value. Fictix does not return it later. | Response: ```json { "id": "0b96f8220f0c46c9a6c33c77", "company_id": "proj_123", "url": "https://example.test/fictix/webhooks", "events": ["fictix.test", "invoice.issued"], "has_secret": true, "active": true, "created_at": "2026-07-01T10:15:30.000000+00:00", "updated_at": "2026-07-01T10:15:30.000000+00:00" } ``` json Secrets are never returned. The response uses `has_secret` so your UI can show whether signing is configured without leaking the secret. ## List webhooks GET`/api/v1/orgs/{slug}/webhooks` ```http GET /api/v1/orgs/{slug}/webhooks X-API-Key: ``` http Response: ```json { "webhooks": [ { "id": "0b96f8220f0c46c9a6c33c77", "company_id": "proj_123", "url": "https://example.test/fictix/webhooks", "events": null, "has_secret": true, "active": true, "created_at": "2026-07-01T10:15:30.000000+00:00", "updated_at": "2026-07-01T10:15:30.000000+00:00" } ], "count": 1 } ``` json `events: null` means the subscription receives every event type. ## Delete a webhook DELETE`/api/v1/orgs/{slug}/webhooks/{webhook_id}` ```http DELETE /api/v1/orgs/{slug}/webhooks/{webhook_id} Authorization: Bearer ``` http Response: ```json { "deleted": true, "webhook_id": "0b96f8220f0c46c9a6c33c77" } ``` json Deleting a webhook removes the subscription. It does not remove historical events from the Fictix event log. ## Send a test event POST`/api/v1/orgs/{slug}/webhooks/{webhook_id}/test` ```http POST /api/v1/orgs/{slug}/webhooks/{webhook_id}/test Content-Type: application/json Authorization: Bearer ``` http Request: ```json { "event_type": "fictix.test" } ``` json Response: ```json { "queued": true, "webhook_id": "0b96f8220f0c46c9a6c33c77", "event_type": "fictix.test", "url": "https://example.test/fictix/webhooks" } ``` json The response means delivery was queued or started. Your receiver should still validate that the callback arrived. ## Delivery headers Webhook POST requests include: ```http Content-Type: application/json X-Fictix-Event: X-Fictix-Timestamp: X-Fictix-Signature: sha256= ``` http `X-Fictix-Signature` is present only when the subscription has a `secret`. The signature is HMAC-SHA256 over the raw request body bytes using the secret stored on the subscription. ## Verify a signature Node example: ```js import crypto from "node:crypto"; export function verifyFictixWebhook({ rawBody, signature, secret }) { if (!signature || !signature.startsWith("sha256=")) { return false; } const expected = crypto .createHmac("sha256", secret) .update(rawBody) .digest("hex"); const received = signature.slice("sha256=".length); return crypto.timingSafeEqual( Buffer.from(received, "hex"), Buffer.from(expected, "hex") ); } ``` js ![](phosphor/regular/warning) Important implementation detail: verify against the raw body bytes, not a JSON-reencoded object. Reencoding can change whitespace or key order and break a valid signature. ## Payload shape Test payloads use this envelope: ```json { "id": "test_1720746930", "event_type": "fictix.test", "company_id": "proj_123", "data": { "message": "This is a test event from Fictix webhooks" }, "timestamp": "2026-07-11T10:15:30.000000+00:00" } ``` json Persisted simulation and runtime events use the canonical envelope: ```json { "id": "evt_abc123", "company_id": "proj_123", "environment_id": null, "schema_version": "1.0", "event_type": "invoice.issued", "occurred_at": "2026-07-11T10:15:30.000000+00:00", "recorded_at": "2026-07-11T10:15:31.000000+00:00", "actor_kind": null, "actor_id": null, "entity_type": "invoice", "entity_id": "inv_123", "correlation_id": "corr_abc123", "parent_id": null, "data": {}, "mutation": null, "projection_refs": [], "audit_meta": null, "layer": 1 } ``` json The stable fields are `id`, `company_id`, `schema_version`, `event_type`, `occurred_at`, and `data`. Treat all other fields as nullable or additive unless your integration needs them. Query the event stream with your project key at: GET`/api/v1/orgs/{slug}/events` ```http GET /api/v1/orgs/{slug}/events ``` http Treat unknown event types as forward-compatible. Store the event, log it, and avoid failing the whole receiver. ## Delivery semantics Fictix delivers webhooks asynchronously. Current delivery behavior: * callback URLs are SSRF-guarded before storage and before delivery * event fanout is tenant scoped * active subscriptions are filtered by event type * deliveries are queued in a durable outbox * the worker claims due deliveries in batches * successful delivery is any `2xx` response * failed delivery is retried with backoff * stuck `sending` rows can be recovered by the worker Each worker delivery uses up to 3 immediate POST attempts. If that worker delivery fails, the durable outbox schedules another worker attempt with a 1, 2, 4, then 8 second backoff. The delivery becomes terminally failed after 5 worker attempts. A receiver must still be idempotent: network retries, the three immediate attempts, and worker recovery can produce duplicate callbacks. Receivers must be idempotent. Network retries and worker recovery can produce duplicate deliveries. Deduplicate by event id. ## Subscription limits Each org can have up to 20 active webhook subscriptions. If the limit is exceeded: ```http 422 Unprocessable Entity ``` http ```json { "detail": "Maximum 20 webhook subscriptions per org" } ``` json The `events` filter stores at most 50 event names per subscription. ## Receiver requirements Your receiver should: * respond within 10 seconds * return a `2xx` status only after accepting the event * verify `X-Fictix-Signature` when a secret is configured * deduplicate by event id * reject stale timestamps if your threat model requires replay protection * avoid fetching user-controlled URLs from the payload * log event id, type, company id, and delivery timestamp ![](phosphor/regular/warning) Do not perform long-running ingestion synchronously inside the webhook handler. Ack the event, enqueue work in your own system, and process it out of band. ## Current event types `fictix.test` is the manual receiver test event. The current canonical financial event types include: ```text invoice.issued payment.received credit_note.issued refund.issued bill.received bill_payment.sent card_transaction.posted reimbursement.submitted bank_deposit.settled bank_withdrawal.settled bank_transfer.completed ``` text Use exact values returned by your project's event stream when subscribing. Unknown event types may be added without a breaking API version, so retain and log them rather than rejecting an otherwise valid delivery. ## Troubleshooting No callbacks arrive: * confirm the project has the `webhooks` entitlement * confirm the subscription is active * confirm the event filter includes the emitted event type * send `fictix.test` * check that your callback URL is publicly reachable over HTTPS * check that your receiver returns a `2xx` response Signatures fail: * verify against raw request bytes * ensure the secret is the exact value used when the subscription was created * do not trim, parse, or reserialize the body before verification Duplicate callbacks: * expected under retry and recovery semantics * deduplicate by event id * make downstream processing idempotent # Provider first requests Public catalog snapshots observed 2026-10-28; first-request templates reviewed 2026-09-07. * **staging** (`https://api-staging.fictix.ai/api/v1/catalog/connector-capabilities`, observed 2026-10-28): 60 catalog entries, 60 listed as available/preview, and 59 marked selectable; status histogram 59 available, 1 preview; fidelity histogram 60 approximate. * **production** (`https://api.fictix.ai/api/v1/catalog/connector-capabilities`, observed 2026-10-28): 60 catalog entries, 60 listed as available/preview, and 59 marked selectable; status histogram 59 available, 1 preview; fidelity histogram 60 approximate. These status and fidelity values are public catalog reports. They do not show that each provider request succeeded, establish production request behavior, or promise strict upstream API parity. The request templates below were tested in staging only. Plan entitlements and company selection still apply. FCI catalog status: staging: preview, not selectable; production: preview, not selectable. It uses `execute` and is not selectable in the standard connector picker. Check capability availability for your company before using its preview request. Start with [Connect your software](/guides/connect-your-software) for keys, SDK base URLs, scopes, and error handling. Read [Provider workflows](/api-reference/provider-workflows) for supported writes, filters, pagination, and limits. [Download all first requests as JSON](/provider-first-requests.json). The file contains public request templates and source release IDs; it contains no keys. ## Request base and authentication Append each table path to `/api/v1/orgs/{slug}/` on your chosen API origin. Use `https://api-staging.fictix.ai` for staging and `https://api.fictix.ai` for production. Send the matching company or organization key as `X-API-Key`. Every first request needs `read`; FCI additionally needs `execute`. For a GET, replace the path below with the one from the table: ```bash curl --fail-with-body -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/stripe/v1/customers" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` bash POST read bodies and their content types are included below the table. These requests do not require a native-write idempotency key. Replace the two FCI body placeholders with the selected company project ID and a unique request ID. ## Provider directory | Provider / ID | Staging-tested first request | Public catalog report | | ----------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | BILL / `billcom` | `GET native/billcom/v3/vendors` | staging: available, selectable; production: available, selectable | | Expensify / `expensify` | `POST native/expensify/Integration-Server/ExpensifyIntegrations` ([body](#expensify-request)) | staging: available, selectable; production: available, selectable | | FreshBooks / `freshbooks` | `GET native/freshbooks/accounting/account/FICTIX/users/clients` | staging: available, selectable; production: available, selectable | | QuickBooks Online / `qbo` | `GET native/qbo/v3/company/test/query?query=SELECT%20*%20FROM%20Customer` | staging: available, selectable; production: available, selectable | | Xero / `xero` | `GET native/xero/api.xro/2.0/Contacts` | staging: available, selectable; production: available, selectable | | Zoho Books / `zoho_books` | `GET native/zoho_books/books/v3/contacts?organization_id=100000000000001` | staging: available, selectable; production: available, selectable | | Chase / `chase` | `GET native/chase/embedded/v1/accounts` | staging: available, selectable; production: available, selectable | | Mercury / `mercury` | `GET native/mercury/accounts` | staging: available, selectable; production: available, selectable | | Plaid / `plaid` | `POST native/plaid/transactions/get` ([body](#plaid-request)) | staging: available, selectable; production: available, selectable | | Wise Business / `wise_business` | `GET native/wise_business/2026Q3/profiles` | staging: available, selectable; production: available, selectable | | Close / `close` | `GET native/close/api/v1/lead/` | staging: available, selectable; production: available, selectable | | HubSpot / `hubspot` | `GET native/hubspot/crm/v3/objects/contacts` | staging: available, selectable; production: available, selectable | | Pipedrive / `pipedrive` | `GET native/pipedrive/api/v2/persons` | staging: available, selectable; production: available, selectable | | Salesforce / `salesforce` | `GET native/salesforce/services/data/v66.0/query?q=SELECT%20Id%2CName%20FROM%20Account` | staging: available, selectable; production: available, selectable | | Brex / `brex` | `GET native/brex/v2/accounts/cash` | staging: available, selectable; production: available, selectable | | Ramp / `ramp` | `GET native/ramp/developer/v1/transactions` | staging: available, selectable; production: available, selectable | | Shopify / `shopify` | `GET native/shopify/admin/api/2025-10/customers.json` | staging: available, selectable; production: available, selectable | | Gmail / `gmail` | `GET native/gmail/gmail/v1/users/me/messages` | staging: available, selectable; production: available, selectable | | Outlook / `outlook` | `GET native/outlook/v1.0/me/events` | staging: available, selectable; production: available, selectable | | Slack / `slack` | `GET native/slack/api/users.list` | staging: available, selectable; production: available, selectable | | GitHub / `github` | `GET native/github/user/repos` | staging: available, selectable; production: available, selectable | | DocuSign / `docusign` | `GET native/docusign/oauth/userinfo` | staging: available, selectable; production: available, selectable | | NetSuite / `netsuite` | `GET native/netsuite/services/rest/record/v1/customer` | staging: available, selectable; production: available, selectable | | SAP / `sap` | `GET native/sap/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner` | staging: available, selectable; production: available, selectable | | Sage Intacct / `sage_intacct` | `POST native/sage_intacct/ia/xml/xmlgw.phtml` ([body](#sage-intacct-request)) | staging: available, selectable; production: available, selectable | | Dropbox / `dropbox` | `POST native/dropbox/2/files/list_folder` ([body](#dropbox-request)) | staging: available, selectable; production: available, selectable | | Google Drive / `google_drive` | `GET native/google_drive/drive/v3/files` | staging: available, selectable; production: available, selectable | | OneDrive / `onedrive` | `GET native/onedrive/v1.0/me/drive/root/children` | staging: available, selectable; production: available, selectable | | FCI / `fci` | `POST execute` ([body](#fci-request)) | staging: preview, not selectable; production: preview, not selectable | | ADP / `adp` | `GET native/adp/hr/v2/workers` | staging: available, selectable; production: available, selectable | | BambooHR / `bamboohr` | `GET native/bamboohr/api/v1/employees` | staging: available, selectable; production: available, selectable | | Deel / `deel` | `GET native/deel/rest/people` | staging: available, selectable; production: available, selectable | | Justworks / `justworks` | `GET native/justworks/v1/members` | staging: available, selectable; production: available, selectable | | Rippling / `rippling` | `GET native/rippling/workers/` | staging: available, selectable; production: available, selectable | | TriNet HR Platform / `trinet` | `GET native/trinet/core/people` | staging: available, selectable; production: available, selectable | | Workday / `workday` | `GET native/workday/staffing/v7/workers` | staging: available, selectable; production: available, selectable | | Google Ads / `google_ads` | `GET native/google_ads/v25/customers:listAccessibleCustomers` | staging: available, selectable; production: available, selectable | | Google Analytics 4 / `google_analytics_4` | `GET native/google_analytics_4/v1beta/properties/1000000001/metadata` | staging: available, selectable; production: available, selectable | | Klaviyo / `klaviyo` | `GET native/klaviyo/api/profiles` | staging: available, selectable; production: available, selectable | | Mailchimp / `mailchimp` | `GET native/mailchimp/3.0/lists` | staging: available, selectable; production: available, selectable | | Meta Ads / `meta_ads` | `GET native/meta_ads/v26.0/me/adaccounts` | staging: available, selectable; production: available, selectable | | Airtable / `airtable` | `GET native/airtable/v0/meta/bases` | staging: available, selectable; production: available, selectable | | Adyen / `adyen` | `POST native/adyen/v72/paymentMethods` ([body](#adyen-request)) | staging: available, selectable; production: available, selectable | | Chargebee / `chargebee` | `GET native/chargebee/api/v2/customers` | staging: available, selectable; production: available, selectable | | Paddle / `paddle` | `GET native/paddle/customers` | staging: available, selectable; production: available, selectable | | PayPal / `paypal` | `GET native/paypal/v1/catalogs/products` | staging: available, selectable; production: available, selectable | | Recurly / `recurly` | `GET native/recurly/accounts` | staging: available, selectable; production: available, selectable | | Square / `square` | `GET native/square/v2/customers` | staging: available, selectable; production: available, selectable | | Stripe / `stripe` | `GET native/stripe/v1/customers` | staging: available, selectable; production: available, selectable | | Gusto / `gusto` | `GET native/gusto/v1/employees` | staging: available, selectable; production: available, selectable | | Excel / `excel` | `GET native/excel/v1.0/me/drive/items/fictix-finance-workbook/workbook/worksheets` | staging: available, selectable; production: available, selectable | | Asana / `asana` | `GET native/asana/api/1.0/tasks` | staging: available, selectable; production: available, selectable | | ClickUp / `clickup` | `GET native/clickup/api/v2/team` | staging: available, selectable; production: available, selectable | | Linear / `linear` | `POST native/linear/graphql` ([body](#linear-request)) | staging: available, selectable; production: available, selectable | | Monday / `monday` | `POST native/monday/v2` ([body](#monday-request)) | staging: available, selectable; production: available, selectable | | Freshdesk / `freshdesk` | `GET native/freshdesk/api/v2/contacts` | staging: available, selectable; production: available, selectable | | Front / `front` | `GET native/front/contacts` | staging: available, selectable; production: available, selectable | | Help Scout / `help_scout` | `GET native/help_scout/v2/customers` | staging: available, selectable; production: available, selectable | | Intercom / `intercom` | `GET native/intercom/contacts` | staging: available, selectable; production: available, selectable | | Zendesk / `zendesk` | `GET native/zendesk/api/v2/users` | staging: available, selectable; production: available, selectable | Relay Financial is unavailable: a direct API contract has not been implemented. FCI uses `execute`. TriNet means **HR Platform (formerly Zenefits)**; it does not describe TriNet PEO. ## POST read bodies The examples use the company-key variables from the connection guide. If you use an organization key, substitute `$FICTIX_ORGANIZATION_API_KEY` in the header. ### Expensify request Expensify: send `application/x-www-form-urlencoded`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/expensify/Integration-Server/ExpensifyIntegrations" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-raw 'requestJobDescription=%7B%22type%22%3A%22get%22%2C%22inputSettings%22%3A%7B%22type%22%3A%22policyList%22%7D%7D' ``` bash ### Plaid request Plaid: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/plaid/transactions/get" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{}' ``` bash ### Sage Intacct request Sage Intacct: send `application/xml`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/sage_intacct/ia/xml/xmlgw.phtml" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/xml" \ --data-raw 'fictix-sdkfirst-readtrue3.0CUSTOMER*10' ``` bash ### Dropbox request Dropbox: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/dropbox/2/files/list_folder" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"path":""}' ``` bash ### FCI request FCI: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/execute" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"org_id":"replace_with_company_project_id","user_id":"fictix-ui","turn_id":"replace_with_unique_request_id","capability_key":"fci.cash","args":{}}' ``` bash ### Adyen request Adyen: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/adyen/v72/paymentMethods" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"merchantAccount":"FictixDemo"}' ``` bash ### Linear request Linear: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/linear/graphql" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"query":"query { teams { nodes { id name } } }"}' ``` bash ### Monday request Monday: send `application/json`. ```bash curl --fail-with-body -sS -X POST \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/monday/v2" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ --data-raw '{"query":"query { boards { id name } }"}' ``` bash ## Check application status An HTTP 200 alone is insufficient for GraphQL, Sage Intacct, Expensify, and execute responses. Inspect GraphQL `errors`, XML operation statuses, Expensify `responseCode`, or execute `status` before using the returned data. See the connection guide for scope and error handling. # Consuming these docs This page is for the agent, not the human reading over its shoulder. It lists every machine-readable entry point into the Fictix docs and how to fetch each one. If you're pointing an agent at Fictix for the first time, see [AI agent integration](/resources/agent-integration) for copy-paste project files (`AGENTS.md`, a Claude Code skill, Cursor rules); this page only covers how to read the docs site itself. ## Read the rules file first `AGENTS.md` at the repo root is the brief: auth layers, canonical entities, pagination and idempotency rules, and links to everything else. Fetch it before crawling anything: ```bash curl -sS https://fictix-docs.vercel.app/AGENTS.md ``` bash `/agents.md` (lowercase) resolves to the same file, for tools that assume the lowercase convention: ```bash curl -sS https://fictix-docs.vercel.app/agents.md ``` bash ## Fetch any page as Markdown Every page on this site is also served as plain Markdown — strip the styled HTML and append `.md` to the page's URL: ```bash curl -sS https://fictix-docs.vercel.app/api-reference/demo-jobs.md curl -sS https://fictix-docs.vercel.app/get-started/quickstart.md ``` bash Use this when you want one page's content in context without pulling in nav chrome, or when you're writing a fetch loop that turns a list of paths into a list of Markdown documents. ## Use the llms.txt index `/llms.txt` is a curated index of the site, grouped by section, with a one-line description per page — read this to decide what to fetch next instead of guessing from the sidebar: ```bash curl -sS https://fictix-docs.vercel.app/llms.txt ``` bash `/llms-full.txt` is every published page concatenated into a single file. Fetch it when you'd rather load the whole docs set once than crawl page by page: ```bash curl -sS https://fictix-docs.vercel.app/llms-full.txt ``` bash ![](phosphor/regular/info) `llms-full.txt` is large. If your context window is tight, read `llms.txt` first and fetch only the pages relevant to the task with `.md`. ## robots.txt `/robots.txt` allows all crawling and points back at the same three entry points, so a generic crawler or an agent that only checks `robots.txt` still finds the machine-readable paths: ```bash curl -sS https://fictix-docs.vercel.app/robots.txt ``` bash ## Quick reference | Want | Fetch | | ---------------------------- | ------------------------------------------------------------ | | The rules brief | `https://fictix-docs.vercel.app/AGENTS.md` (or `/agents.md`) | | One page, as Markdown | `https://fictix-docs.vercel.app/.md` | | An index of the whole site | `https://fictix-docs.vercel.app/llms.txt` | | The whole site, concatenated | `https://fictix-docs.vercel.app/llms-full.txt` | | Crawl/entry-point pointers | `https://fictix-docs.vercel.app/robots.txt` | # Skill pack The Fictix skill pack is a small folder of plain-text files that teaches an agent the Fictix API without it re-deriving conventions from this whole site: a skill definition, a copy of the repo-level rules file, and a set of working examples. It's more than the single skill snippet on [AI agent integration](/resources/agent-integration) — that page shows you how to write your own minimal skill by hand; this pack is the version Fictix publishes and maintains directly, ready to drop in. ## What's inside | File | Purpose | | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | [`SKILL.md`](/fictix-skill-pack/SKILL.md) | Skill frontmatter plus a distilled brief: auth layers, canonical entities, read/paginate/export conventions, error handling. | | [`AGENTS.md`](/fictix-skill-pack/AGENTS.md) | A copy of the repo-root [`AGENTS.md`](/AGENTS.md) — the full brief, unabridged. | | [`examples.md`](/fictix-skill-pack/examples.md) | Working `curl`/`jq` examples: reading data, company info, catalog discovery, CSV export, idempotent demo-job creation, and staging organization keys. | Each file is a plain download — fetch it directly, or have an agent fetch it for you: ```bash curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/SKILL.md curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/AGENTS.md curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/examples.md ``` bash ![](phosphor/regular/warning) None of these files should ever contain a real API key. Read `FICTIX_PROJECT_API_KEY` or `FICTIX_ORGANIZATION_API_KEY` from the environment at runtime; treat a committed `SKILL.md` or `AGENTS.md` like any other source file a teammate — or the internet — can read. ## Install for Claude Code Claude Code loads a skill from a folder under `.claude/skills/`. Create the folder and copy the pack's files into it: ```bash mkdir -p .claude/skills/fictix curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/SKILL.md -o .claude/skills/fictix/SKILL.md curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/AGENTS.md -o .claude/skills/fictix/AGENTS.md curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/examples.md -o .claude/skills/fictix/examples.md ``` bash Claude Code discovers `.claude/skills/fictix/SKILL.md` automatically and loads the rest of the folder as supporting files the skill can reference. ## Install for other agents Most other coding agents (Cursor, Codex, and similar tools) read a plain `AGENTS.md` from the repo root instead of a skills folder. Drop the pack's `AGENTS.md` there directly: ```bash curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/AGENTS.md -o AGENTS.md ``` bash If you already have a repo-root `AGENTS.md`, append the Fictix section instead of overwriting your existing rules. Keep `examples.md` nearby (for example alongside the `AGENTS.md` you just saved) so an agent that wants a worked example can find one without leaving the repo. ## Keeping it current The pack is a snapshot of the same source Fictix documents everywhere else — `AGENTS.md` and the canonical entity list in particular can change as the API evolves. Re-run the `curl` commands above periodically, or point an agent at [`/llms-full.txt`](/llms-full.txt) for the full current docs set instead of relying on a pack fetched once and never refreshed. # Versions and compatibility Fictix has two distinct versions that integration teams should track. | Version | What it identifies | Where to read it | | ------------------------ | ----------------------------------------- | ----------------------------------------------------------- | | API deployment release | The deployed backend build | `/ready` deployment metadata and release notes | | Partner contract version | The normalized external response contract | `contract_version` in partner responses and `/openapi.json` | ## Current partner contract The current partner contract version is: ```text 2026-07-17 ``` text ![](phosphor/regular/info) It is a date-based string. Do not coerce it to a number. ## Compatibility rules * New optional fields and new catalog items can be added without changing the contract version. * Clients must ignore response fields they do not recognize. * A renamed, removed, or behaviorally incompatible public field/route requires a contract-version change and a changelog entry with migration guidance. * Pin your integration tests to a staging company and record the deployed release, partner contract version, selected services, and catalog response. The OpenAPI document specifies supported request bodies, auth schemes, and stable top-level response envelopes. Generated company records remain flexible by design; inspect the catalog for the selected service/object schema. ## Upgrade checklist Before moving an integration to a new Fictix release: 1. Read the [Changelog](/resources/changelog). 2. Fetch the staging `/openapi.json` and compare the partner paths you use. 3. Re-run your create/load/export or runtime test against a staging company. 4. Verify error handling still accepts both string and structured `detail`. 5. Store the release and `contract_version` with your test result. ## Deprecation discipline Fictix will document a replacement route/field and a client action before a public contract removal. Internal project/admin routes are not part of this compatibility commitment unless they are explicitly added to these developer docs. For Console-created companies, use the [Console onboarding flow](/get-started/console-onboarding) to rotate keys and test a staging integration before changing production. # AI agent integration Many teams building against Fictix now do most of the integration work through an AI coding agent — Claude Code, Cursor, Codex/OpenAI Codex, or something similar — rather than hand-writing every request. Agents work best when they have a short, explicit brief instead of having to infer conventions from a full doc site, so this page gives copy-paste starting points for pointing your agent at Fictix: a docs-ingestion trick, and ready-made project files for Claude Code and Cursor. ## Machine-readable docs Every page on this site is available as raw Markdown — useful for pasting into a prompt or letting an agent fetch it directly: * Append `.md` to any page URL to get that page as plain Markdown, e.g. `https://fictix-docs.vercel.app/api-reference/demo-jobs.md`. * [`/llms.txt`](/llms.txt) is a curated index of the docs, meant to help an agent decide what to fetch next. * [`/llms-full.txt`](/llms-full.txt) is the entire documentation set concatenated into one file, sized for one-shot ingestion into an agent's context window when you'd rather hand over everything up front than let the agent crawl page by page. ![](phosphor/regular/warning) None of the snippets below should ever contain a real project API key. If you paste one of these files into a repo that's shared or public, read the key from an environment variable at runtime instead of hardcoding it — treat a committed `AGENTS.md`, `SKILL.md`, or `.cursor/rules/*.mdc` file exactly like any other source file a teammate (or the internet) can read. ## AGENTS.md Drop a condensed version of this into your own project's `AGENTS.md` (or append it to an existing one) so any agent that reads that file picks up the essentials before it writes a single request. ````markdown ## Fictix API Fictix is a synthetic-company data API (accounting, banking, CRM, payroll, tasks). Get a project slug and API key from the Fictix Console (`console.fictix.ai` → Company settings → Company API keys) — never invent or construct these values. Environment variables this project expects: ```bash export FICTIX_API_ORIGIN="https://api.fictix.ai" # or api-staging.fictix.ai export FICTIX_PROJECT_SLUG="" export FICTIX_PROJECT_API_KEY=" Company settings > Company API keys>" ``` Auth header for all runtime/data requests: ```http X-API-Key: ``` Runtime data lives under `GET $FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/{entity}`. Canonical entity names — use these, not the deprecated aliases in parentheses: accounts, bank_accounts, bank_transactions, bills, credit_memos, customers, customer_payments (alias: payments), deals, employees, invoices, items, journal_entries, payroll_runs, tasks, vendor_payments (alias: bill_payments), vendors Page with `limit`/`offset` and continue while `offset + data.length < count`. If this project also creates/controls companies via `POST /api/v1/demo-jobs` (approved control-plane access only, separate from the project API key above), use the Fictix-supplied `FICTIX_ADMIN_API_KEY` as `X-Admin-Key`. The signed-in Console uses a managed WorkOS bearer session for its own actions; do not ask for or copy that session token. Every call must carry an `Idempotency-Key` header. Reuse the same key only to retry the exact same request body — a different body with a reused key returns `409`. Never reuse a placeholder key like `demo` or `test` across unrelated calls. Full reference: https://fictix-docs.vercel.app/llms-full.txt ```` markdown ## Claude Code skill Save this as `.claude/skills/fictix-api/SKILL.md` in your own repo to teach Claude Code these facts as a loadable skill instead of relying on it re-deriving them from context each time. ````markdown --- name: fictix-api description: Facts and conventions for calling the Fictix synthetic-company API — base URL, auth header, and canonical entity names. --- # Fictix API Fictix serves synthetic-company data (accounting, banking, CRM, payroll, tasks) for a company created or selected in the Fictix Console. ## Config Read these from the environment — never hardcode a real key in this file or in generated code: ```bash FICTIX_API_ORIGIN # e.g. https://api.fictix.ai or https://api-staging.fictix.ai FICTIX_PROJECT_SLUG # from Console > Connect to your builder FICTIX_PROJECT_API_KEY # from Console > Company settings > Company API keys ``` ## Auth Runtime/data requests use a project API key in this header: ```http X-API-Key: ``` Scope it to `/api/v1/orgs/{FICTIX_PROJECT_SLUG}/...` routes only. Don't send this key to `/api/v1/demo-jobs` control-plane routes, and don't send a control-plane credential to `/orgs/{slug}` routes. The external control-plane credential (approved partners only) is `FICTIX_ADMIN_API_KEY` as `X-Admin-Key`; never invent other credential variable names or solicit a Console session token. ## Canonical entities Use these names (not the parenthetical legacy aliases) when reading or generating code against entity data: accounts, bank_accounts, bank_transactions, bills, credit_memos, customers, customer_payments (alias: payments), deals, employees, invoices, items, journal_entries, payroll_runs, tasks, vendor_payments (alias: bill_payments), vendors ## Common request ```bash curl -sS "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/customers?limit=100&offset=0" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" ``` Page with `limit`/`offset`; continue while `offset + data.length < count`. ## Idempotency (control-plane only) `POST /api/v1/demo-jobs` requires a stable `Idempotency-Key` header. Reuse a key only to retry the identical request body; a different body with a reused key returns `409`. ## Further reading Full docs as Markdown: https://fictix-docs.vercel.app/llms-full.txt (or fetch a single page by appending `.md` to its URL). ```` markdown ## Cursor rules Save this as `.cursor/rules/fictix.mdc` so Cursor applies it automatically whenever you're editing files that touch the Fictix integration. ```markdown --- description: Fictix API conventions — base URL, auth header, canonical entities, and idempotency rules for demo-jobs. globs: ["**/*fictix*", "**/lib/fictix/**"] alwaysApply: false --- # Fictix API conventions - Base URL and credentials come from the environment, never hardcoded: `FICTIX_API_ORIGIN`, `FICTIX_PROJECT_SLUG`, `FICTIX_PROJECT_API_KEY`. - Runtime/data requests authenticate with `X-API-Key: ` and are scoped to `/api/v1/orgs/{FICTIX_PROJECT_SLUG}/...`. - Use canonical entity names in new code: accounts, bank_accounts, bank_transactions, bills, credit_memos, customers, customer_payments, deals, employees, invoices, items, journal_entries, payroll_runs, tasks, vendor_payments, vendors. Treat `payments` and `bill_payments` as deprecated aliases for `customer_payments` and `vendor_payments`. - Page list endpoints with `limit`/`offset`; keep paging while `offset + data.length < count`. - Any call to `POST /api/v1/demo-jobs` must include a stable `Idempotency-Key` header. Only retry with the same key if the request body is unchanged — a changed body with a reused key returns `409`. - Error `detail` may be a plain string or a `{ code, message }` object; handle both shapes. - Fictix ships an official MCP sidecar (`python -m fictix_mcp`) — prefer it over hand-wrapping these REST endpoints. It is a local process, not a hosted endpoint. Setup: https://fictix-docs.vercel.app/for-agents/mcp ``` markdown ## Every other coding agent — same brief, different filename The same three ingredients — the env vars, the `X-API-Key` header, and the canonical entity list — are the whole brief regardless of tool. The AGENTS.md snippet above is the content; the only thing that changes is which file your tool reads it from: | Tool | Where to put the brief | | ------------------------------------------- | ------------------------------------------------------------------------------------------- | | Codex, Amp, opencode, and most newer agents | `AGENTS.md` at the repo root — the emerging cross-tool standard | | GitHub Copilot | `.github/copilot-instructions.md`; the Copilot coding agent also reads `AGENTS.md` directly | | Windsurf | `.windsurf/rules/fictix.md` (legacy single-file: `.windsurfrules`) | | Cline | `.clinerules` (single file or folder) | | Gemini CLI | `GEMINI.md` | | JetBrains Junie | `.junie/guidelines.md` | | Replit Agent | `replit.md` | Or skip the copy-paste and pull the maintained version straight from the [skill pack](/for-agents/skill-pack): ```bash curl -sS https://fictix-docs.vercel.app/fictix-skill-pack/AGENTS.md -o AGENTS.md ``` bash Most of these tools also speak MCP (Copilot in VS Code, Windsurf, Cline, Gemini CLI). The [MCP sidecar](/for-agents/mcp) (`python -m fictix_mcp`) works with all of them — each client has its own config file shape, but the server env vars are always the same ones documented on that page (`FICTIX_BASE_URL`, `FICTIX_DEFAULT_SLUG`, `FICTIX_PROJECT_API_KEY`). ## Browser-based app builders (Lovable, v0, Bolt, Replit) Prompt-driven app builders generate **frontend** code, and a Fictix project API key must never ship in frontend code — see [browser safety](/get-started/authentication). The pattern that works is a thin server-side proxy: the builder's server leg (Lovable → Supabase Edge Function, v0 → Next.js route handler, Bolt/Replit → server endpoint) holds the key as a secret and relays requests; the browser only ever talks to your proxy. Paste this into your builder's knowledge/system-prompt area: ```text This app reads business data from the Fictix API (synthetic-company data: accounting, banking, CRM, payroll, tasks). Rules: - Never call api.fictix.ai from browser code, and never put FICTIX_PROJECT_API_KEY in client-side source or public env vars. - Create a server-side proxy (edge function / route handler) that reads FICTIX_API_ORIGIN, FICTIX_PROJECT_SLUG, and FICTIX_PROJECT_API_KEY from server secrets. - The proxy calls GET {FICTIX_API_ORIGIN}/api/v1/orgs/{FICTIX_PROJECT_SLUG}/data/{entity} with header "X-API-Key: {FICTIX_PROJECT_API_KEY}" and returns the JSON to the browser. Support limit/offset query params. - Valid entities: accounts, bank_accounts, bank_transactions, bills, credit_memos, customers, customer_payments, deals, employees, invoices, items, journal_entries, payroll_runs, tasks, vendor_payments, vendors. - Page with limit/offset; keep fetching while offset + data.length < count. - Error "detail" may be a plain string or a { code, message } object — handle both shapes. Full API docs: https://fictix-docs.vercel.app/llms-full.txt ``` text These tools can't run a local MCP sidecar, and there's no hosted MCP endpoint yet — the proxy brief above is the supported path today. # Demo jobs A demo job is the external resource that represents one generated company. Internally, Fictix stores projects, org-scoped runtime routes, generated datasets, normalized entity shadows, native service records, simulation state, events, validation results, and scenario manifests. Externally, a partner should not need to stitch those pieces together. The `demo_job` resource is the stable wrapper around them. ## Lifecycle ```text created -> generating -> ready \-> error ``` text Most current demo-job creates perform generation during the request and return a ready resource. Clients should still handle polling because larger or future async jobs may return before generation is complete. ## Creating a job POST`/api/v1/demo-jobs` ```http POST /api/v1/demo-jobs Idempotency-Key: ``` http The request has four main blocks: ```json { "company": {}, "generation": {}, "simulation": {}, "scenarios": [] } ``` json ### `company` Describes the real or hypothetical business you want Fictix to synthesize. Important fields: * `name` * `domain` * `apollo_id` * `archetype` * `industry` * `employee_count` * `annual_revenue` * `customer_accounts` * `company_age_months` * `selected_services` * `dials` ### `generation` Controls generated data scale and quality. Important fields: * `employees` * `months_of_history` * `quality_preset` Quality presets: * `clean` * `realistic` * `messy` * `stress_test` ### `simulation` Initializes the simulation clock and dials. Important fields: * `seed` * `start_date` * `dials` ### `scenarios` Optional planted-scenario inputs used for QA and eval flows. Example: ```json { "scenarios": [ { "id": "ghost-employee", "params": { "months": 1 } } ] } ``` json ## Important response fields ```json { "object": "demo_job", "contract_version": "2026-07-17", "regeneration_required": false, "id": "8f3a2d11", "status": "ready", "links": { "self": "/api/v1/demo-jobs/8f3a2d11", "status": "/api/v1/demo-jobs/8f3a2d11/status", "quality": "/api/v1/demo-jobs/8f3a2d11/quality" }, "company": {}, "runtime": {}, "dataset_summary": {}, "quality": {}, "credentials": {} } ``` json ### `id` The partner-facing demo job identifier. Today it maps to the underlying project id. Store this as the primary key in your integration. ### `regeneration_required` When `true`, the retained company predates the current accounting contract. Regenerate it before importing its data into a workflow that requires the current contract. ### `links` Stable follow-up links. Prefer using links over hardcoding paths when building a generic client. ### `runtime` Contains lower-level identifiers: * `project_id` * `slug` * `publish_id` Use `runtime.slug` for runtime/org routes. Use `publish_id` for human handoff URLs. ### `dataset_summary` Contains record counts and entity availability. Use this to decide which loaders to run. ### `quality` Summarized validation status. Use the full quality endpoint for check-level details. ### `credentials` ![](phosphor/regular/warning) Includes a runtime API key for optional lower-level runtime access. Store it only when required. ## Listing jobs GET`/api/v1/demo-jobs?limit=100&offset=0` ```http GET /api/v1/demo-jobs?limit=100&offset=0 ``` http The list endpoint returns visible jobs for the authenticated user/operator. ## Status polling GET`/api/v1/demo-jobs/{job_id}/status` ```http GET /api/v1/demo-jobs/{job_id}/status ``` http Use status polling when: * generation may be slow * a UI needs a progress/status state * a worker wants to wait before loading entities ## Resetting jobs POST`/api/v1/demo-jobs/{job_id}/reset` ```http POST /api/v1/demo-jobs/{job_id}/reset ``` http Reset requires an explicit confirmation body: ```json { "confirm": true } ``` json ![](phosphor/regular/warning) Reset clears generated runtime state and returns the job to a clean state. It is intended for iterative demo/test flows, not for ordinary data refresh. Prefer clone when you need a new independent company. ## Cloning jobs POST`/api/v1/demo-jobs/{job_id}/clone` ```http POST /api/v1/demo-jobs/{job_id}/clone ``` http Clone creates a new project with a new runtime key and seed. Use clone when you want a similar company shape but isolated state. ## Publishing jobs POST`/api/v1/demo-jobs/{job_id}/publish` ```http POST /api/v1/demo-jobs/{job_id}/publish ``` http Publish returns stable handoff metadata: * `publish_id` * dashboard URL * staging URLs Use this when a PM, solutions engineer, or customer engineer needs a stable reference to a generated company. # Simulation and time Simulation lets a Fictix company continue to change after initial generation. Use it when a static dataset is not enough. Examples: * advance a company clock * generate new activity over time * create webhook events * plant scenarios * inspect an event stream * score whether another product found known issues ## Simulation state Every simulation has: * a company/project id * a clock * a seed * dial configuration * autonomous status * checkpoint history Read status: GET`/api/v1/demo-jobs/{job_id}/simulation/status` ```http GET /api/v1/demo-jobs/{job_id}/simulation/status ``` http ## Starting simulation POST`/api/v1/demo-jobs/{job_id}/simulation/start` ```http POST /api/v1/demo-jobs/{job_id}/simulation/start ``` http Example body: ```json { "seed": 42, "start_date": "2026-01-01", "dials": { "chaos": 0.1, "fraud": 0.0 } } ``` json ![](phosphor/regular/info) If simulation state already exists, the route returns the existing normalized state rather than creating a duplicate simulation. ## Advancing time POST`/api/v1/demo-jobs/{job_id}/simulation/advance` ```http POST /api/v1/demo-jobs/{job_id}/simulation/advance ``` http Example: ```json { "days": 30, "hours": 0, "minutes": 0 } ``` json The delta must be positive and within the configured advance limit. ## Single step POST`/api/v1/demo-jobs/{job_id}/simulation/step` ```http POST /api/v1/demo-jobs/{job_id}/simulation/step ``` http Use step when you want one deterministic increment rather than a large time advance. ## Pause and resume POST`/api/v1/demo-jobs/{job_id}/simulation/pause` POST`/api/v1/demo-jobs/{job_id}/simulation/resume` ```http POST /api/v1/demo-jobs/{job_id}/simulation/pause POST /api/v1/demo-jobs/{job_id}/simulation/resume ``` http Pause stops autonomous progression. Resume restarts it. ## Dials PATCH`/api/v1/demo-jobs/{job_id}/simulation/dials` ```http PATCH /api/v1/demo-jobs/{job_id}/simulation/dials ``` http Example: ```json { "trickle_rate": "10x", "fraud": 0.2, "chaos": 0.1 } ``` json ![](phosphor/regular/warning) Dials control behavior intensity. Treat unknown dials as unstable unless they are documented in the catalog or scenario docs. ## Checkpoints Create checkpoint: POST`/api/v1/demo-jobs/{job_id}/simulation/checkpoint` ```http POST /api/v1/demo-jobs/{job_id}/simulation/checkpoint ``` http List checkpoints: GET`/api/v1/demo-jobs/{job_id}/simulation/checkpoints` ```http GET /api/v1/demo-jobs/{job_id}/simulation/checkpoints ``` http Restore checkpoint: POST`/api/v1/demo-jobs/{job_id}/simulation/restore/{checkpoint_id}` ```http POST /api/v1/demo-jobs/{job_id}/simulation/restore/{checkpoint_id} ``` http Use checkpoints for eval loops, demos, and repeatable QA. ## Events Read event feed: GET`/api/v1/demo-jobs/{job_id}/events?limit=50` ```http GET /api/v1/demo-jobs/{job_id}/events?limit=50 ``` http Read one event: GET`/api/v1/demo-jobs/{job_id}/events/{event_id}` ```http GET /api/v1/demo-jobs/{job_id}/events/{event_id} ``` http Events are the bridge between simulation and host-product reactions. A future customer-facing integration can subscribe to webhooks, poll events, or both. ## Scenario manifest Scenario injection can plant truth into the company. The manifest describes what was planted. GET`/api/v1/demo-jobs/{job_id}/simulation/manifest` ```http GET /api/v1/demo-jobs/{job_id}/simulation/manifest ``` http Use the manifest for: * test harness setup * evaluating detected findings * explaining what broke * reproducing failed app behavior # Load Fictix data into your app For a product that already calls a provider API or SDK, use [Connect your software](/guides/connect-your-software) and its provider-specific request directory. This page covers loading canonical data and exports. This guide covers ingestion design for products that want Fictix data inside their own database. Start by creating or selecting the company in the [Fictix Console](https://console.fictix.ai), then copy its server-side runtime setup from **Connect to your builder**. This guide assumes you have a project slug and project `X-API-Key`; it does not require a shared admin key. ## Choose JSON or CSV Use JSON when: * you control the loader * nested lines/applications matter * you want full record fidelity * you need lineage fields Use CSV when: * your product already has CSV importers * a user needs spreadsheet review * a batch pipeline expects files ## JSON loader pattern 1. Get record counts from runtime info or job status. 2. Determine load order. 3. Page each entity. 4. Upsert into your database. 5. Store Fictix lineage. 6. Reconcile counts. Pseudocode: GET`/api/v1/orgs/{slug}/data/{entity}` ```python import requests def load_entity(origin: str, slug: str, api_key: str, entity: str): offset = 0 limit = 500 total = None rows = [] seen_ids = set() while total is None or offset < total: response = requests.get( f"{origin}/api/v1/orgs/{slug}/data/{entity}", headers={"X-API-Key": api_key}, params={"limit": limit, "offset": offset}, timeout=30, ) response.raise_for_status() page = response.json() data = page.get("data") current_total = page.get("count") if not isinstance(data, list) or type(current_total) is not int or current_total < 0: raise ValueError("Expected an entity page with data and a non-negative count") if "offset" in page and page["offset"] != offset: raise ValueError(f"Requested offset {offset}, received {page['offset']}") if len(data) > limit: raise ValueError(f"Received {len(data)} rows for limit {limit}") if total is not None and current_total != total: raise ValueError(f"Entity count changed during pagination: {total} to {current_total}") total = current_total if len(rows) + len(data) > total: raise ValueError(f"Fetched more than the reported entity count {total}") for row in data: record_id = row.get("id") if isinstance(row, dict) else None if record_id is None or not str(record_id).strip(): raise ValueError(f"Record at offset {offset} has no id") record_id = str(record_id) if record_id in seen_ids: raise ValueError(f"Duplicate record id across pages: {record_id}") seen_ids.add(record_id) rows.append(row) if not data and offset < total: raise ValueError(f"Pagination stopped at {offset} before count {total}") for row in data: upsert_row(entity, row, lineage=page.get("lineage")) offset += len(data) if len(rows) != total: raise ValueError(f"Fetched {len(rows)} rows, but count is {total}") return len(rows) ``` python ## CSV loader pattern Export one entity: GET`/api/v1/orgs/{slug}/export/{entity}?format=csv` ```bash curl "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/export/customers?format=csv" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -o customers.csv ``` bash Then run your import: ```bash your-importer customers.csv --entity customers ``` bash For accounting workflows, prefer normalized line files when using CSV bundles from runtime export packs. ## Mapping notes ### Customers Map: * `id` to external id * `name` or `display_name` to customer display name * `email` to primary email * address fields to billing address * payment terms to customer settings ### Invoices Map: * invoice header fields first * line items second * customer references after customers are loaded * item references after items are loaded Load **credit\_memos** after invoices. Each applied credit memo links to an invoice through **invoice\_id**. Confirm each memo's **customer\_id** and **currency** match the invoice. Sum **amount\_applied** from credit memos by **invoice\_id**; keep the invoice's **amount\_paid** as cash receipts only. Validate in integer cents: ```text applied_credit_memos = sum(amount_applied for credit_memos with invoice_id == invoice.id) invoice.total - invoice.amount_paid - applied_credit_memos - invoice.balance = 0 ``` text Do not fold applied credits into **invoice.amount\_paid**. ### Credit memos The **credit\_memos** entity contains applied accounts receivable credits linked to invoices. Read **invoice\_id** and **amount\_applied** from each memo and group the applied amount by invoice. Load invoices first so the linked invoice is available when importing a memo. ### Bills Load vendors before bills. Validate: ```text total - amount_paid - balance = 0 ``` text ### Payments Use canonical names: * `customer_payments`, not `payments` * `vendor_payments`, not `bill_payments` This prevents downstream double-counting. ### Bank transactions Load bank accounts first. Every `bank_account_id` should resolve to a bank account or bank-designated account record. ### Journal entries Validate that each entry balances: ```text sum(debits) = sum(credits) ``` text ## Reconciliation after load After loading, compare: * Fictix source count * fetched count * imported count * rejected count * duplicate count Example reconciliation record: ```json { "entity": "invoices", "fictix_count": 200, "fetched_count": 200, "imported_count": 200, "rejected_count": 0 } ``` json ## Handling partial loads Make loaders resumable by storing: * project slug or job id * entity * last offset * page size * import checkpoint id * error detail ![](phosphor/regular/warning) Do not restart from scratch unless your upsert semantics are idempotent. ## Avoiding synthetic data leakage Mark imported records: * source: `fictix` * synthetic: `true` * demo tenant id * generated timestamp ![](phosphor/regular/warning) Do not send Fictix synthetic emails to real email workflows unless your product has an explicit demo-mode mail sink. ## Approved control-plane integrations POST`/api/v1/demo-jobs` If your backend has approved `/api/v1/demo-jobs` access, it can create jobs and use normalized entity pages/exports directly. ![](phosphor/regular/warning) Keep that credentialed flow on your server; the Console remains the customer-facing source for organizations, projects, key rotation, billing, and support context. # Capability matrix Fictix is intentionally capability-driven: the available records and native-shaped routes depend on the generated company and selected services. ![](phosphor/regular/warning) Do not hard-code a provider/object assumption from a marketing list. Discover the current capability before issuing a runtime request. ## Discovery surface Catalog endpoints are public and are safe to call before you have a company key: | Need | Endpoint | | ----------------------------------------- | ------------------------------------------------------------------------ | | Canonical entities and aliases | `GET /api/v1/catalog/entities` | | Selectable/unsupported connector status | `GET /api/v1/catalog/connector-capabilities` | | Service families and visible record types | `GET /api/v1/catalog/services` | | Object display/schema metadata | `GET /api/v1/catalog/services/{service_id}/objects/{object_type}/schema` | | File-oriented export packs | `GET /api/v1/catalog/export-packs` | ## Access matrix The [provider directory](/api-reference/provider-first-requests) contains first-request templates reviewed for the September 7, 2026 staging release and tested in staging only. Public catalog snapshots observed October 28, 2026 list 60 entries in both environments (59 marked available/selectable and one FCI preview); all 60 report approximate fidelity. These catalog reports do not verify provider request behavior or promise strict upstream parity. The [workflow reference](/api-reference/provider-workflows) documents supported staging operations and limits. Relay Financial is unavailable. FCI uses execute and remains marked as preview with `ui_selectable: false` in both catalogs. In the connector catalog, `status` and `ui_selectable` describe availability. `native_supported` and `contract_surfaces` identify the available transports. `fidelity`, `parity_status`, and `limitations` describe compatibility; these are separate from the object-schema labels below. Query the API origin you will use rather than assuming the production and staging catalogs match. | Surface | Purpose | Credential | Source of truth | | ----------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------ | | Catalog | Discover entities, services, and schemas | None | `/api/v1/catalog/*` | | Normalized runtime data | Read one Console-created company's records | Project `X-API-Key` | `/api/v1/orgs/{slug}/data/{entity}` | | Runtime exports | Load JSON, CSV, and supported packs | Project `X-API-Key` plus applicable entitlement | `/api/v1/orgs/{slug}/export/*` | | Native-shaped runtime | Exercise selected provider-like APIs | Project `X-API-Key` plus `native.genome` entitlement | `/api/v1/orgs/{slug}/native/{service}/...` | | Execute transport | Call a named capability, including FCI | Runtime `X-API-Key` with `execute` plus `read` or `write` for the capability | `/api/v1/orgs/{slug}/execute` | | Console management | Create/control jobs in the signed-in Console | Console-managed WorkOS bearer session (not copyable) | `/api/v1/demo-jobs/*` | | Demo-job control plane | Create/control normalized jobs from an external integration | Fictix-supplied `FICTIX_ADMIN_API_KEY` / `X-Admin-Key` | `/api/v1/demo-jobs/*` | In staging, an organization `X-API-Key` can also use the normalized data, export, and native runtime rows above for companies owned by that organization. The same scopes and entitlements apply. Its company directory is `GET /api/v1/organizations/{org_id}/companies`; it does not grant Console or demo-job management access. See [One key for multiple companies](/get-started/organization-api-keys). ## Fidelity labels The catalog can mark a native object schema as one of these levels: | Label | Meaning | | ------------- | ----------------------------------------------------------------------- | | `native` | The object has provider-shaped fields documented by the catalog schema. | | `generic` | The object is available but uses Fictix's generic field projection. | | `unsupported` | The connector is not selectable for the requested product state. | ![](phosphor/regular/info) Treat `native` as shape compatibility, not a claim that every upstream endpoint or vendor side effect is implemented. Test the exact method/path your product uses against a staging company before promoting your own integration. ## Recommended capability check GET`/api/v1/catalog/connector-capabilities` GET`/api/v1/catalog/services/{service_id}/objects/{object_type}/schema` ```bash curl -sS "https://api.fictix.ai/api/v1/catalog/connector-capabilities" | jq curl -sS "https://api.fictix.ai/api/v1/catalog/services/qbo/objects/invoice/schema" | jq ``` bash Then use the project-specific key issued through the [Fictix Console](https://console.fictix.ai) to make the runtime call: GET`/api/v1/orgs/{slug}/native/{service}/...` ```bash curl -sS \ "https://api.fictix.ai/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/qbo/invoice" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` bash ## Unsupported or partial behavior Build integrations to handle `400`, `404`, and `422` without assuming a missing resource is a transport outage. Use the capability/catalog response to decide whether to hide a feature, choose a generic projection, or use another service. For a product-ready integration, persist the catalog version and selected service set alongside the imported data. That makes later support and replay work deterministic. # Simulation, scenarios, and events Simulation endpoints let a generated company evolve over time and expose an event stream that another application can consume. Base path: ```text /api/v1/demo-jobs/{job_ref} ``` text ## Start simulation POST`/api/v1/demo-jobs/{job_ref}/simulation/start` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/start ``` http Body: ```json { "seed": 42, "start_date": "2026-01-01", "dials": { "chaos": 0.1, "fraud": 0.0 } } ``` json If simulation exists, returns current state. ## Get simulation status GET`/api/v1/demo-jobs/{job_ref}/simulation/status` ```http GET /api/v1/demo-jobs/{job_ref}/simulation/status ``` http Returns simulation clock, dial config, pause/resume state, and autonomous status. ## Advance simulation POST`/api/v1/demo-jobs/{job_ref}/simulation/advance` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/advance ``` http Body: ```json { "days": 30, "hours": 0, "minutes": 0 } ``` json Errors: * `400` when the delta is not positive * `400` when the delta exceeds the configured maximum advance window ## Step simulation POST`/api/v1/demo-jobs/{job_ref}/simulation/step` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/step ``` http Runs one simulation step. ## Pause simulation POST`/api/v1/demo-jobs/{job_ref}/simulation/pause` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/pause ``` http ## Resume simulation POST`/api/v1/demo-jobs/{job_ref}/simulation/resume` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/resume ``` http ## Update dials PATCH`/api/v1/demo-jobs/{job_ref}/simulation/dials` ```http PATCH /api/v1/demo-jobs/{job_ref}/simulation/dials ``` http Body: ```json { "trickle_rate": "10x", "fraud": 0.2, "chaos": 0.1 } ``` json ## Create checkpoint POST`/api/v1/demo-jobs/{job_ref}/simulation/checkpoint` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/checkpoint ``` http ## List checkpoints GET`/api/v1/demo-jobs/{job_ref}/simulation/checkpoints` ```http GET /api/v1/demo-jobs/{job_ref}/simulation/checkpoints ``` http ## Restore checkpoint POST`/api/v1/demo-jobs/{job_ref}/simulation/restore/{checkpoint_id}` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/restore/{checkpoint_id} ``` http ## List scenarios GET`/api/v1/demo-jobs/{job_ref}/scenarios` ```http GET /api/v1/demo-jobs/{job_ref}/scenarios ``` http Returns scenario definitions available for the job/runtime. ## Generate scenario suggestions POST`/api/v1/demo-jobs/{job_ref}/scenarios/generate` ```http POST /api/v1/demo-jobs/{job_ref}/scenarios/generate ``` http Use this to ask Fictix for scenario candidates appropriate to the company. ## Inject simulation scenarios POST`/api/v1/demo-jobs/{job_ref}/simulation/scenarios` ```http POST /api/v1/demo-jobs/{job_ref}/simulation/scenarios ``` http Body: ```json { "scenarios": [ { "id": "ghost-employee", "params": { "months": 1 } } ] } ``` json Returns or updates planted truth for the simulation. ## Get scenario manifest GET`/api/v1/demo-jobs/{job_ref}/simulation/manifest` ```http GET /api/v1/demo-jobs/{job_ref}/simulation/manifest ``` http ![](phosphor/regular/info) The manifest describes planted truth. Use it to build eval harnesses and to explain what the target application should have detected. ## Delete scenario DELETE`/api/v1/demo-jobs/{job_ref}/simulation/scenarios/{scenario_id}` ```http DELETE /api/v1/demo-jobs/{job_ref}/simulation/scenarios/{scenario_id} ``` http Removes a scenario when supported by the backend state. ## List events GET`/api/v1/demo-jobs/{job_ref}/events` ```http GET /api/v1/demo-jobs/{job_ref}/events ``` http Query params: | Name | Type | Description | | ------------ | ------- | --------------------------------------- | | `limit` | integer | Max events to return. | | `cursor` | string | Cursor for pagination, where supported. | | `event_type` | string | Filter by event type, where supported. | ## Get event GET`/api/v1/demo-jobs/{job_ref}/events/{event_id}` ```http GET /api/v1/demo-jobs/{job_ref}/events/{event_id} ``` http Use events to: * replay activity into another product * inspect what simulation generated * debug webhook delivery * build eval timelines # Provider workflows > **Staging release:** The workflows on this page were reviewed for the 60-entry staging catalog on September 7, 2026 and verified in staging. Public catalog snapshots observed October 28, 2026 list 60 entries in both staging and production (59 marked available/selectable and one FCI preview). Catalog status does not verify production requests or establish strict API parity. Check each provider in the [first-request directory](/api-reference/provider-first-requests) and query your environment's catalog before connecting. Start with [Connect your software](/guides/connect-your-software) for API keys, base URLs, SDK configuration, and response handling. The directory provides the request templates tested in staging and the public catalog report for each environment, including a downloadable JSON file. The production catalog report does not mean these request templates were tested in production. Relay Financial is unavailable until a direct API contract is implemented. These are synthetic workflows with documented limits. The public catalogs report 60 entries each: 59 marked available/selectable and one FCI execute preview. These are catalog annotations, not a claim that all provider routes work in both environments. FCI is not selectable in the standard connector picker; confirm capability availability for your company before using it. Use an organization runtime API key for companies in your workspace, or a company runtime API key for one company. Send it as `X-API-Key`. Native reads need `read`; native writes need `write`; execute reads need `execute` and `read`, while execute writes need `execute` and `write`. These keys authenticate to Fictix. The Chase `access_token` argument is optional and is not needed for synthetic banking operations. Discover the current runtime catalog at `/api/v1/catalog/connector-capabilities` and export packs at `/api/v1/catalog/export-packs`. A connector's availability does not mean that every endpoint of the upstream vendor is implemented. ## Native routes Append the provider path to: ```text /api/v1/orgs/{company_slug}/native/{provider} ``` text | Providers | Workflow covered by generated-company verification | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | Airtable | List bases; create and retrieve a task record | | Asana | List tasks; create and retrieve a task | | BILL | List vendors; create and retrieve a vendor | | Chargebee | Read customers, invoices and transactions; manage customers, items, prices and subscription state | | ClickUp | Discover workspaces and lists; create, update, comment on and delete tasks | | DocuSign | Create, send and void envelopes; update recipients and download documents | | Dropbox, OneDrive | Read generated finance files; upload/download exact bytes and manage files/folders | | Brex | Cash accounts and transactions; manage virtual cards and vendor ACH accounts; post and retrieve transfers | | Chase | Read accounts and balances; manage recipients; post ACH, domestic wires and internal transfers | | Mercury | Manage payment recipients; post ACH, domestic wires and internal transfers; read ledger entries and update notes | | Wise Business | Discover a business profile and USD balance; create recipients and quotes; create, retrieve and cancel unfunded transfers | | Monday | Query boards and users; manage items, column values, groups and comments with GraphQL | | Excel | List worksheets; write and retrieve cell values | | Rippling | Read workers and users; create and update departments; create, update and delete teams | | BambooHR | Discover employees and field metadata; create and update personnel; filter and page through the roster | | Expensify | Discover and create policies; create draft expenses and reports; export and download report contents | | Gmail | Read, send and modify messages; manage drafts, labels and threads; retrieve raw MIME, attachments and history | | Google Drive | List files; create and retrieve a file | | Google Ads | Discover a test account; create, query, update and remove shared budgets and Search campaigns through v25 REST | | Google Analytics 4 | Validate and collect test web events; query their counts, users and page views with the Data API | | Meta Ads | Discover an ad account; create, retrieve, update, archive and delete campaigns with daily budgets | | Gusto | List employees; create and retrieve an employee | | HubSpot | List contacts; create and retrieve a contact | | NetSuite | List customers; create and retrieve a customer | | Outlook | List events; create and retrieve an event | | Pipedrive | Read and manage persons, organizations, deals, activities and products | | Plaid | Read transactions; refresh and retrieve the resulting event | | QuickBooks, Shopify, Square, Stripe | List customers; create and retrieve a customer | | Slack | Read the generated member directory through `api/users.list` | | Ramp | Read transactions; create and retrieve a card | | Salesforce | Query and manage CRM sObjects; compose requests with references and rollback | | Sage Intacct | XML queries and pagination; create, update and delete customers, vendors and GL accounts | | Xero | List contacts; create and retrieve a contact | | Zendesk | Manage users, organizations and tickets; append and retrieve ticket comments | Native writes require a nonempty idempotency key. Send a fresh `Idempotency-Key` header for each operation unless its workflow documents another header or body field. Reuse the key only when retrying the same operation and payload. ### Rippling Use `/api/v1/orgs/{slug}/native/rippling` as the REST base URL and send `Rippling-Api-Version: 2024-08-01` (also the default). `GET /workers/` and `GET /users/` read generated personnel; `GET /workers/{id}/` and `GET /users/{id}/` retrieve one record. Worker and user IDs remain stable and are distinct. Supported worker fields include status, work email, title, start/end dates and references to users, managers and departments. `GET /departments/` derives the initial departments from canonical employee labels. Create with `POST /departments/` and update with `PATCH /departments/{id}/`; both require `name`. An optional `parent_id` must reference an existing department, and `reference_code` persists. Renaming a department updates matching canonical employee department labels while keeping its Rippling ID. `GET /teams/`, `POST /teams/`, `GET /teams/{id}/`, `PATCH /teams/{id}/` and `DELETE /teams/{id}/` provide persistent team management. Team creation requires a name; updates change only submitted name/parent fields. Group names are unique within each resource. Parents must exist, cycles are rejected, and a team with children cannot be deleted. Writes require an `Idempotency-Key` and leave payroll and cash records unchanged. Lists accept `limit` (1–100; default 50), `cursor`, and `order_by` using `id`, `created_at` or `updated_at`, comma-separated with optional `asc`/`desc`. Follow the returned `next_link`; the terminal page omits it. Cursors are scoped to the company, resource and query. Workers additionally accept one `filter` comparison on status, work\_email, user\_id, created\_at or updated\_at. String fields use quoted `eq`/`ne`; timestamps also support `gt`, `ge`, `lt` and `le`. For example, `filter=status eq 'ACTIVE'` returns active workers. Compound predicates and unsupported fields return explicit errors. Expand supported relations using `expand=user,department` on workers, `expand=parent` on departments/teams, or two levels such as `expand=manager.user,department.parent`. The supported worker relations are user, manager, department and teams; generated team membership is empty. Hiring/termination writes, team-assignment writes, payroll, benefits, compensation, custom objects, permission administration and other API versions are outside this workflow. Missing optional fields are omitted. Response and request fixtures come from [Rippling's published OpenAPI](https://developer.rippling.com/docs/rest-beta.yaml). The official `@rippling/rippling-sdk` client can use this base URL and a Fictix `X-API-Key` header; supply `Idempotency-Key` on each write. ### BambooHR Use JSON requests under `/api/v1/orgs/{slug}/native/bamboohr/api/v1` with a Fictix runtime key. `GET /employees` lists the generated company roster; `GET /employees/directory` returns the published directory and its fieldset. `GET /meta/fields` identifies supported aliases and their synthetic numeric IDs. `GET /employees/{id}` returns only `id` unless `fields=firstName,workEmail,...` is supplied. Numeric field IDs from the metadata also work. Employee internal IDs are immutable and distinct from editable `employeeNumber` values. An integration key is not bound to an employee, so `GET /employees/0` returns `{"id":"0"}`. Create employees with `POST /employees` and at least `firstName` and `lastName`. The response is 201 with the new employee and a `Location` header. Update only submitted fields with `POST /employees/{id}`. Writable fields are first/last/ preferred name, work/home email and phone, mobile phone, employee number, department, division, location, job title, hire date and Active/Inactive status. Work email and employee number must be unique within the BambooHR roster. Writes require an `Idempotency-Key`. Personnel changes remain visible through canonical company records; they do not create payroll or cash transactions. `DELETE /employees/{id}` hides that BambooHR resource and returns 204, preserving shared personnel and financial history used by other integrations. The roster accepts `page[limit]` (1–2500), `page[after]` or `page[before]` using returned cursors and links. Filters use the comma-separated `filter[ids]` form, `filter[status]=active|inactive`, or case-insensitive substring matching for first/last name, job title name, work email, department/division/location name and employee number. Multiple filters combine with AND. Sort on employeeId, firstName, lastName, preferredName, jobTitleName or status, comma-separated with `-` for descending order. Cursors are bound to the company and query; changing or deleting the boundary employee invalidates that cursor. Additional roster fields use `fields=workEmail,departmentName,...` and have different aliases from single-employee reads. Use the documented comma-separated query forms. Unknown requested read fields are omitted. Unsupported write fields and query forms are rejected. Directory sharing is fixed for the synthetic company; `onlyCurrent=false` includes future hires, while inactive employees stay hidden. An empty directory returns 404. Request `Accept: application/json`; XML, custom fields, photos, payroll, benefits, effective-dated job/compensation history and permission administration are outside this workflow. Endpoint fixtures are extracted from [BambooHR's published OpenAPI](https://openapi.bamboohr.io/main/latest/docs/openapi/public-openapi.yaml). ### Expensify Send every job as `POST` to `/api/v1/orgs/{slug}/native/expensify/Integration-Server/ExpensifyIntegrations`. Use `Content-Type: application/x-www-form-urlencoded` and a `requestJobDescription` form field containing the job JSON. Plain JSON request bodies are unsupported. For example, a policy discovery job is: ```json {"type":"get","inputSettings":{"type":"policyList"}} ``` json Authenticate with a Fictix runtime key in `X-API-Key`. A vendor `credentials` object may contain synthetic `partnerUserID` and `partnerUserSecret` strings for client compatibility; it is discarded before execution and persistence. Fictix does not authenticate against an Expensify account. Read scopes authorize `get` and `download` jobs. `create` and `file` jobs require `write` and a fresh `Idempotency-Key`. Retrying the same job with that key returns its saved result. Policy discovery returns one company policy plus any policies you create. Use `type: "create"` with `inputSettings.type: "policy"`, `policyName` and optional `plan: "team"` or `"corporate"` to create another. The result contains `responseCode: 200`, `policyID` and `policyName`. For policy details, use a `get` job with `inputSettings.type: "policy"`, a `policyIDList` array and a `fields` array. Supported fields are `employees`, `categories`, `reportFields`, `tags` and `tax`. Membership comes from the generated company's employees; the first employee is the synthetic policy owner. `policyList` also accepts `userEmail` and `adminOnly`. User emails must belong to that company. Categories, tags and tax start empty; the title is the only report field. Policy membership and configuration updates are unsupported. A report creation job uses `inputSettings.type: "report"`, `policyID`, a generated `employeeEmail`, `report: {"title": "Travel"}` and an `expenses` array. Each expense contains `date` in `YYYY-MM-DD`, `merchant`, `currency: "USD"` and positive integer `amount` in cents. The report stays `Open` / `Draft`. A standalone expense creation job uses `inputSettings.type: "expenses"`, `employeeEmail` and `transactionList`. These rows use `created` for their date and may also include `reportID`, `policyID`, `externalID`, `category`, `tag`, `billable`, `reimbursable` and `comment`. A report must belong to that employee; any supplied policy must agree with the report. The result includes string `transactionID` values and integer-cent amounts. `externalID` is an export label; use `Idempotency-Key` for retries. Each report or expense batch is atomic and contains at most 100 expenses. These writes do not reimburse employees, debit cash, allocate payments or post journal entries. To export reports, send a `file` job with `onReceive: {"immediateResponse": ["returnRandomFileName"]}`, `inputSettings.type: "combinedReportData"`, `inputSettings.filters.reportIDList` as comma-separated report IDs, and `outputSettings.fileExtension: "csv"` or `"txt"`. Pass a separate `template` form field using the documented FreeMarker subset, for example: ```text Report,Merchant,Amount <#list reports as report> <#list report.transactionList as expense> ${report.reportID},${expense.merchant},${expense.amount} ``` text Supported constructs are report and expense loops, `addHeader` conditions, scalar field interpolation, whitespace trim directives, division by 100, `?string("0.00")`, boolean `?c` / `?string`, and string `?replace`. Use the template's quoting and replacement syntax when fields may contain CSV commas, quotes or newlines. Other expressions and directives fail explicitly. Templates are limited to 8 KiB, exports to 100 reports and output to 1 MiB. The export response is a plain filename. Send a `download` job with that `fileName` and optional `fileSystem: "integrationServer"` to retrieve the exact saved UTF-8 CSV or text bytes. Downloads remain unchanged when reports later receive new expenses. Filenames identify company-local stored exports. Business failures return `responseCode` and `responseMessage` in a JSON body, so clients must inspect `responseCode` even when HTTP is 200. Malformed transport bodies and Fictix authentication failures use HTTP errors. FX, tax, approvals, custom report fields, arbitrary FreeMarker, reconciliation, reimbursements, SFTP, email actions and webhooks are unsupported. The contract follows [Expensify's job reference](https://integrations.expensify.com/Integration-Server/doc/index.html) and [report template reference](https://integrations.expensify.com/Integration-Server/doc/export_report_template.html). ### Wise Business Use `/api/v1/orgs/{slug}/native/wise_business/2026Q3` with a Fictix runtime key in `X-API-Key`. This contract follows Wise's published 2026Q3 API. Legacy `v1`, `v2` and `v3` paths and response formats are outside this contract. `GET /profiles` returns the company-local business profile. Use its integer `id` for `GET /profiles/{id}` and `GET /profiles/{id}/balances?types=STANDARD`. The single standard USD balance reflects the company's first canonical USD bank account ordered by source ID. It is not an aggregate of all bank accounts. `types=SAVINGS` returns an empty list; balance creation and jars are unsupported. Create a USD recipient with `POST /accounts`: ```json { "profile": 10001, "currency": "USD", "type": "aba", "accountHolderName": "Example Supplier", "ownedByCustomer": false, "details": { "legalType": "BUSINESS", "abartn": "064000020", "accountNumber": "40000000000", "accountType": "CHECKING", "address": { "country": "US", "firstLine": "1 Example Way", "city": "New York", "state": "NY", "postCode": "10025" } } } ``` json The recipient's `id` is an integer, and its name is returned in `name.fullName`. `GET /accounts` returns a `content` array and seek positions; use `seekPositionForNext` on the next request and stop when `content` is empty. Pages contain at most 20 recipients. Filters include profile, currency, active state and ownership, with `id` or `currency` ordering. `DELETE /accounts/{id}` deactivates the recipient; direct GET still returns it with `active: false`. Account details are validated structurally without a real bank lookup. Create a quote at `POST /profiles/{id}/quotes` with `sourceCurrency: "USD"`, `targetCurrency: "USD"` and exactly one positive `sourceAmount` or `targetAmount`. Amounts have at most two decimal places. Quotes use a synthetic rate of one, zero fees and a 30-minute lifetime. They do not represent live Wise pricing. Supply `targetAccount` when creating the quote or attach it with `PATCH /profiles/{id}/quotes/{quoteId}`. An expired or used quote cannot change. `POST /transfers` accepts `targetAccount`, `quoteUuid`, a UUID `customerTransactionId` and optional `details.reference`. The result remains `incoming_payment_waiting`. The quote changes from `PENDING` to `ACCEPTED` and can create only one transfer, including during concurrent requests. Reusing `customerTransactionId` for the same normalized request retrieves that transfer; changing the request returns a conflict. HTTP retries also require the original `Idempotency-Key`. Read transfers with `GET /transfers/{id}` or list them using profile, status, currency, creation-date, limit and offset filters. Cancel an unfunded transfer with `PUT /transfers/{id}/cancel` and an empty object. Its state becomes `cancelled`. These instructions never debit a bank account, reserve cash, create a payment allocation or post a journal entry. Funding, settlement, FX, other currencies, KYC, dynamic requirements, batches and webhooks are unsupported. The response fixture is extracted from the [published Wise OpenAPI](https://docs.wise.com/_bundle/api-reference/@latest/index.json?download). USD recipient field names follow Wise's [published account example](https://www.postman.com/transferwise/transferwise-s-public-workspace/request/u3rk4k2/create-usd-account). ### Salesforce, Pipedrive, Chargebee and Zendesk These providers expose native routes as well as export packs. Point an HTTP client or an SDK with a configurable endpoint at its Fictix native base URL. Keep the runtime key in a server environment. Browser clients use an origin-bound browser token, as described in the native browser access guide. | Provider ID | Example path after the native base | Mutation format | | ------------ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | `salesforce` | `/services/data/v66.0/query?q=SELECT%20Id,Name%20FROM%20Account` | JSON; create with POST, update with PATCH, delete with DELETE | | `pipedrive` | `/api/v2/persons` | JSON; create with POST, update with PATCH, delete with DELETE | | `chargebee` | `/api/v2/customers` | JSON or `application/x-www-form-urlencoded`; writes use POST | | `zendesk` | `/api/v2/tickets` | JSON wrapped in `ticket`, `user` or `organization`; create with POST, update with PUT | Chargebee also accepts `chargebee-idempotency-key`, the official SDK's header. If both idempotency headers are present they must agree. A replay returns `chargebee-idempotency-replayed: true`. Its Product Catalog 2 subscription workflow is: create a customer, item family, plan item and item price; POST `/api/v2/customers/{id}/subscription_for_items` with `subscription_items`; then retrieve, update, cancel or reactivate that subscription. Invoice collection, proration, refunds and scheduled billing are outside this contract. Salesforce supports scalar SOQL selection and comparisons, one-field ordering, `COUNT()`, `LIMIT`, and `OFFSET`. Composite requests support references such as `@{account.id}` and `allOrNone` rollback. Joins, general aggregates, tooling, Apex, provisioning and bulk APIs are outside this contract. Pipedrive supports core collection operations and name search with cursor pagination. Saved filters, merges and changelogs are outside this contract. Zendesk comments are appended through ticket creation or update; closed tickets reject edits. Email delivery, attachments, bulk jobs and automations are outside this contract. These are approximate synthetic APIs. Shared customer fields update the same canonical company records. CRM organization/contact views can originate from one canonical customer, and deleting a provider record hides that provider's record while preserving the shared entity. Spreadsheet export IDs retain their existing import contract and can differ from native API IDs. ### Configure accounting before an external bank payout Brex transfers to external vendors, Chase ACH/wire payments, and Mercury recipient payments need a company posting account. This is a prerequisite for the staging accounting workflow below; production payout accounting has not been qualified by these staging checks. An organization owner or administrator selects the account in **Settings → Company → Bank payout posting account** and chooses **Save account**. Reload the page and confirm the same account is displayed before sending a payment. If that control is absent in your environment, customer setup is unavailable there; do not work around it by copying a Console session token or using an operator credential. Choose an active canonical general-ledger account in the payout bank account's currency. Bank accounts, accounts receivable, accounts payable, and accounts linked to a bank are excluded. Saving the choice does not validate a future payment's currency; that check happens when the payment is posted. The setting is shared by these three providers within one company, so change it explicitly when the intended posting account changes. **Clear saved account** removes the choice and prevents subsequent external payouts until an account is saved again. These controls do not change the selected services, plan, or key scopes. A native caller still needs an entitled plan and a key with `write` scope, plus the route's idempotency key. A missing posting-account choice returns HTTP 400 with a message asking you to configure the counteraccount. The native error envelope depends on the provider: Brex includes code `CONFIGURATION_REQUIRED`, Chase reports `invalid_parameter` with context, and Mercury includes the message in `errors.general`. Fix the company setting before retrying; do not treat that response as a completed payment. Internal transfers between company bank accounts are a separate workflow. After a successful external payment, retrieve the provider operation and bank balance, then check canonical `bank_transactions`, `journal_entries`, and `gl_entries` or the exported books. The posting debits the chosen account and credits the bank's cash account. Readback must show one payment and one balanced posting, including after the provider's documented retry/replay behavior. ### Brex cash and cards Read cash accounts at `/v2/accounts/cash` and transactions at `/v2/transactions/cash/{account_id}`. Create virtual cards through `/v2/cards` with an owner from `/v2/users`. Card limits and lock, unlock and terminate operations are persistent and shared with execute. Create a vendor at `/v1/vendors` with ACH `payment_accounts`; use its returned `payment_instrument_id` in `/v1/transfers`. Transfers are processed immediately in the synthetic ledger and debit the selected account once. Amounts in the native API use integer cents; execute amounts use dollars. Physical card delivery, PANs, approval policies, international wires and external bank settlement are outside this contract. ### Mercury payments Use `/api/v1/accounts` and `/api/v1/recipients` for account and recipient discovery. Create recipients with `POST /api/v1/recipients`, using `name`, `emails` and ACH `electronicRoutingInfo` or `domesticWireRoutingInfo`, including their US address. Recipient creation requires an `Idempotency-Key` header. Do not put `idempotencyKey` in the recipient JSON; that body field is supported for Mercury payment and transfer requests. Send money with POST `/api/v1/account/{account_id}/transactions`; pass a positive dollar `amount`, `recipientId`, `paymentMethod` and a unique `idempotencyKey`. Repeating the body key returns HTTP 409 with the original transaction. If you also send an `Idempotency-Key` header, the two keys must agree. POST `/api/v1/transfer` moves funds between project accounts and returns the paired debit and credit transactions. Synthetic payments settle immediately; the balance and ledger update in the same transaction. Native and execute calls share recipients and money movements. Check delivery, international payments, approvals and external bank settlement are outside this contract. ### Gmail mailbox Use `/gmail/v1/users/me` for the project's mailbox. POST `/messages/send` with `{"raw":""}` to store a sent message. Drafts use `{"message":{"raw":"..."}}`; sending a draft removes it and creates a sent message in the same thread. Native calls and execute email operations share messages, labels, drafts and history. No email leaves the synthetic mailbox. Message and thread lists support `maxResults`, `pageToken`, repeated `labelIds`, and basic search terms such as `from:`, `subject:`, `in:`, `has:attachment`, `after:` and `before:`. Terms are combined with AND; prefix a term with `-` to exclude it. History requires `startHistoryId` and accepts `historyTypes` and `labelId`. Raw MIME attachments retain their bytes. Pub/Sub watches, mailbox settings, media upload sessions and advanced search are outside this contract. ### Chase Embedded Payments The native contract targets JPMorgan Embedded Payments: accounts and recipients use `/embedded/v1/accounts` and `/embedded/v1/recipients`; transactions use `/embedded/v2/transactions`. Discover the synthetic `clientId` and account IDs from the account list. Read balances at `/accounts/{id}/balances`. Create a recipient with `partyDetails`, `type: "RECIPIENT"` and a US checking or savings `account` containing `routingInformation`. Supported rails are ACH and domestic WIRE. POST a transaction with a unique `transactionReferenceId`, `debtorAccountId`, `recipientId`, positive dollar `amount`, `currency: "USD"` and `type`. A TRANSFER instead uses `creditorAccountId`. A successful synthetic transaction is immediately COMPLETED and updates cash once. Retrying with the same HTTP idempotency key replays the response; reusing the reference in a new request returns 409. Account provisioning, RTP, international payments, verification and bank settlement are outside this contract. The response schema fixtures retain the published account/recipient 1.0.58 and transaction 2.0.58 contracts from [JPMorgan's documentation](https://developer.payments.jpmorgan.com/api/embedded-finance-solutions/embedded-payments/embedded-payments/recipients). ### Monday GraphQL POST `/v2` with `query`, optional `variables` and `operationName`. Queries need `read`; mutations need `write` and an `Idempotency-Key`. The selected GraphQL operation determines the required scope. Variables, aliases, fragments and directives are supported. Mutation fields commit together; any field failure rolls back all writes in that request. Read `boards`, `users`, `items` and `items_page`; continue a page with `next_items_page`. Create items and groups, change column values, append/delete updates, archive/delete items, or move them between groups on their board. Generated boards expose status, due date, one assignee and priority text columns. Read `columns.settings_str` for status labels and numeric indices. Column writes accept JSON-encoded strings. Filters accept string or numeric `CompareValue` variables, with equality, text containment, empty checks and ordering. Queries are limited to 64 KiB, 12 selection levels and 1,000 expanded selections. Page size is at most 500. Board creation, custom columns, teams, subitems, subscriptions and webhooks are outside this contract. See the upstream [item pagination contract](https://developer.monday.com/api-reference/reference/items-page). ### Sage Intacct XML POST XML to `/ia/xml/xmlgw.phtml`. Use the usual Intacct `request`, `control`, `operation` and `function` envelope with `dtdversion` 3.0. The Fictix runtime key authenticates the request. Vendor passwords and session values are discarded; do not provide real Intacct credentials. Reads need `read`; any write function needs `write`. Writes require an `Idempotency-Key`, or `true` with a stable control ID. A retry replays identical XML bytes. Read CUSTOMER, VENDOR, GLACCOUNT, APBILL, ARINVOICE, GLBATCH, CHECKINGACCOUNT and SAVINGSACCOUNT through `read`, `readByName`, `readByQuery`, `query` and `readMore`. Create/update/delete is supported for customers, vendors and GL accounts. `getAPISession` returns a synthetic session. XML queries support explicit fields, scalar comparisons, boolean filters and ordering. Legacy `readByQuery` accepts one comparison; use `query` for compound filters. Return format is XML. Requests are limited to 1 MiB and 50 functions; responses to 4 MiB and 10,000 records. DTD and entity declarations are rejected. With `transaction="true"`, any failed function aborts the entire operation. With `false`, successful functions persist independently. Inspect each XML result's status, including on HTTP 200. The execute API additionally creates/posts bills, creates invoices, allocates payments, makes deposits/transfers and creates/posts balanced journals. These operations use the same canonical documents and cash accounts as native reads. Payment allocations reject overpayment; cash movements reject insufficient funds; journal posting updates GL balances once. Bill/invoice posting does not automatically produce GL journal entries. Historical balance snapshots, tax calculation, multi-currency accounting and external settlement are outside this contract. The optional official JavaScript SDK test covers the XML customer lifecycle; SDKs that enforce an Intacct hostname need a custom HTTP transport. See the upstream [XML gateway reference](https://developer.intacct.com/web-services/). Execute aging uses current outstanding balances and due-date buckets. The trial balance uses current GL balances. The cash-flow response reports bank ledger inflows/outflows for the requested dates; it does not classify investing and financing activity or claim a full accounting cash-flow statement. ### Tasks, envelopes and files ClickUp starts at `/api/v2/team`, followed by `/team/{id}/space` and `/space/{id}/list`. Create tasks with POST `/api/v2/list/{id}/task`; update with PUT `/api/v2/task/{id}`. Dates use Unix milliseconds. Updates to assignees use `{"assignees":{"add":[123],"rem":[]}}`; creation takes an array of IDs. Status, assignee and tag filters accept repeated `statuses[]`, `assignees[]` and `tags[]` parameters. List pages contain at most 100 tasks. DocuSign account discovery is GET `/oauth/userinfo`. Use the returned account ID under `/restapi/v2.1/accounts/{accountId}` for envelopes and templates. POST `/envelopes` accepts `documents`, `recipients` and `emailSubject`, or a `templateId` and `templateRoles`. PUT `/envelopes/{id}` supports `sent` and `voided` transitions. Documents download as bytes. Signing ceremonies and Connect webhooks are outside this contract. Dropbox uses POST for its native file RPCs. `/2/files/upload` takes raw bytes and a `Dropbox-API-Arg` JSON header containing `path` and `mode`. `/2/files/download` takes the same header with a path or file ID, and returns bytes plus a `Dropbox-API-Result` metadata header. Metadata, download and folder-list RPCs need only the `read` scope. Both Dropbox API and content host paths use this one Fictix native base. OneDrive uses Graph paths such as `/v1.0/me/drive/root/children` and `/v1.0/me/drive/root:/example.txt:/content`. PUT content takes raw bytes; GET content returns those exact bytes. Folder creation uses POST children with `name` and `folder: {}`. PATCH an item to rename or move it; DELETE removes it and its descendants. `If-Match` accepts the item's current `eTag`. The file APIs preserve arbitrary bytes, including JSON and empty files. Uploads are limited to 1 MiB per request. Generated finance files are seeded independently for each storage provider. Upload sessions, sharing, delta synchronization and subscriptions are outside this contract. ## Freshdesk support workflows Use `/native/freshdesk/api/v2` beneath the company route. Contact and company CRUD, agent discovery, ticket CRUD, ticket replies and private/public notes persist in the generated company. Creating a ticket with an email can create its requester. Read and edit conversations through their ticket, and resolve tickets using status `4` (`5` closes them). Lists support `page`, `per_page` and a next-page `Link`; ticket lists default to the past 30 days. Use `include=description,requester` on lists and `include=conversations` on a ticket detail to inspect its first ten conversations. Supply a Fictix runtime key and `Idempotency-Key` for writes. Reusing a key with the same request preserves the original result. Replies and notes remain synthetic. Attachments, configured custom fields, automation, structured chat bodies and analytics are outside this adapter's contract. ## Front discussion and task workflows Use `/native/front` beneath the company route. Contacts support CRUD and handle changes, with email/phone handles linked to canonical customer fields. Read `inboxes` and `teammates`, then create a `discussion` with its initial `comment`, or a `task` with a description and optional future `due_at` timestamp. Both require an inbox or a list of teammates. Comments, assignment, archival, trash and deletion persist. A conversation must be in the trash before permanent deletion; only a comment's creating Fictix credential may edit its body. Collections use Front's `_results` response and `_pagination.next` links. Contact timestamp filters and conversation status filters use the JSON `q` parameter. Write calls require `Idempotency-Key`, including DELETE operations that carry a JSON body. Mail channel sending/import, attachments, contact lists, custom field definitions, ticketing status categories and automations are outside this contract. ## Help Scout inbox workflows Use `/native/help_scout/v2` beneath the company route. Create a customer, read its `Resource-ID` response header, and retrieve it using the `Location` header. Creates have empty response bodies. The same headers survive idempotent replay. Customer PATCH requests take an array of operations; PUT overwrites profile fields, clearing omitted fields while retaining email and phone entries. Read `mailboxes` and `users`, then create a conversation with a customer and at least one thread. Customer messages, replies, notes, chat and phone threads, assignment, status updates and draft publication persist. Conversation PATCH takes one operation object. Published replies reactivate a conversation unless they specify another status; imported customer threads preserve its status. Lists use HAL `_embedded`, `_links` and `page` objects; conversation lists default to active conversations. Use `status=all` to include closed ones. Writes require `Idempotency-Key`. Customer deletion removes that provider's customer and associated conversations from its API. Advanced search, configured custom fields, teams, organizations, attachments, automated audit threads, merging, reports and webhooks are outside this contract. The inbox and user directory are synthetic, and no live messages are sent. ## Intercom customer and conversation workflows Use `/native/intercom` beneath the company route with `Intercom-Version: 2.16`. Contacts, company upserts and contact/company associations share canonical customer records. Create a conversation from a contact, then add replies or notes, assign an admin, close, reopen or snooze it. Creates return HTTP 200; conversation creation returns a message with its `conversation_id`. `POST contacts/search`, `POST conversations/search` and `POST companies/list` are reads and accept read-only keys without an idempotency header. Search cursors bind to the company and query. Contact timestamp searches compare UTC calendar days; the stored timestamps retain seconds. Writes require `Idempotency-Key`. Custom attributes, teams, brands, attachments, quick replies, contact archive/merge, Messenger, automation, reports and webhooks are excluded. For server-side code using `intercom-client@7.0.3`, its public configuration supports the native base URL and headers: ```typescript const client = new IntercomClient({ token: "fictix", baseUrl: `${companyBaseUrl}/native/intercom`, headers: { Authorization: "", "X-API-Key": process.env.FICTIX_RUNTIME_KEY, "Intercom-Version": "2.16", }, }); const contact = await client.contacts.create( { role: "user", email: "customer@example.test", name: "Test customer" }, { headers: { "Idempotency-Key": crypto.randomUUID() } }, ); ``` typescript The token placeholder satisfies the SDK constructor; the runtime key authenticates the request. The additional header selects 2.16 because this SDK release's typed version option ends at 2.14. All conversations remain synthetic. ## Close CRM workflows Use `/native/close/api/v1` beneath the company route. Leads, contacts, opportunities, tasks and Note activities support creation, independent reads, updates with `PUT`, and deletion. A lead can include nested contacts when created; a contact or opportunity without a lead creates one atomically. Invalid nested records roll back the entire write. Lead deletion removes its Close children while retaining the underlying shared business records. Opportunity values use integer minor units. Monthly values are multiplied by 12 for annualized totals; list aggregates cover all matching records before pagination. The synthetic workspace provides one Sales pipeline, Active/Won/Lost opportunity statuses, and Potential/Qualified/Bad Fit lead statuses. A transition to Won supplies `date_won` when missing and respects `X-TZ-Offset`. Reopening a won opportunity preserves its existing win date. Tasks support lead and outgoing-call records, assignment and completion; these records do not place calls. Lists use `_limit` (1–100), `_skip` (0–10000), `data` and `has_more`. Contacts can be filtered by lead. Opportunities, tasks and notes support the documented simple relationship and date filters implemented in the connector; unsupported query parameters return a validation error. `_fields` accepts top-level field names. Status, pipeline and user discovery are read-only. The official `closeio==2.1` Python package can use its public `API` class with a custom base URL and the Fictix runtime header: ```python from closeio_api import API client = API(company_native_base + "/api/v1/", api_key="fictix", tz_offset="0") client.session.headers["X-API-Key"] = runtime_key lead = client.post("lead", {"name": "Example"}, headers={"Idempotency-Key": operation_id}) ``` python This covers plaintext notes, core CRM records and task workflows. Rich-text HTML input, attachments, configured custom fields, advanced/text search, grouped reports, merge, moving contacts with linked records, automation, configuration writes and webhooks are outside this contract. Browser callers use origin-bound short-lived bearer tokens. No live Close requests or communications are sent. ## GitHub issue workflows Use `/native/github` beneath the company route. `GET user/repos` discovers the synthetic private repository `fictix/demo-workspace`. Issues share the company's task records and support creation, edits, assignment, labels, milestones, comments, closing and reopening. Label and milestone configuration supports creation, editing and deletion. Issue numbers remain stable across task edits. The default REST API version is `2022-11-28`. Set `X-GitHub-Api-Version` to `2026-03-10` for the newer contract; it accepts `assignees` and rejects the removed singular `assignee` write field. Responses identify the selected version. Lists support page/per-page parameters and scoped `Link` URLs. JSON GET responses support `ETag` and `If-None-Match`. Writes require `Idempotency-Key`. The official Octokit client can use a custom base URL and public request hook: ```typescript const client = new Octokit({ baseUrl: `${companyBaseUrl}/native/github` }); client.hook.before("request", (options) => { options.headers["X-API-Key"] = process.env.FICTIX_RUNTIME_KEY; options.headers["X-GitHub-Api-Version"] = "2026-03-10"; }); const issue = await client.rest.issues.create({ owner: "fictix", repo: "demo-workspace", title: "Example task", headers: { "Idempotency-Key": crypto.randomUUID() }, }); ``` typescript Repository and collaborator discovery are read-only. This contract covers issue tracking and Markdown bodies. Git transport, repository content, branches, commits, pull requests, reviews, Actions, GraphQL, rendered HTML, notifications and webhooks are excluded. Repository URLs describe synthetic metadata; these workflows do not create repositories or issues on github.com. ## Linear issue and project workflows Send GraphQL JSON bodies to `/native/linear/graphql` beneath the company route. Read `teams`, `users` and `workflowStates` to discover the synthetic Engineering team, generated employees and Backlog/Todo/In Progress/Done/Canceled states. Issue mutations share canonical tasks; project renames update the names on their tasks. Markdown comments, labels, assignment, parent issues and project membership persist and can be read through their related records. Queries accept read-only runtime keys without `Idempotency-Key`. Mutations require write scope and an idempotency key. Reuse that key for a retry; choose a new key for each different mutation. All mutation fields in one request commit together or roll back together. Aliases, fragments, variables and named operation selection use GraphQL schema execution. The official `@linear/sdk@93.0.1` uses public endpoint and header options: ```typescript const client = new LinearClient({ apiKey: "fictix", apiUrl: `${companyBaseUrl}/native/linear/graphql`, headers: { "X-API-Key": process.env.FICTIX_RUNTIME_KEY }, }); const teams = await client.teams(); client.client.setHeader("Idempotency-Key", crypto.randomUUID()); const created = await client.createIssue({ title: "Example task", teamId: teams.nodes[0].id, }); const issue = await created.issue; ``` typescript Lists use Relay `nodes`, `edges` and `pageInfo`, with `first`/`after` or `last`/`before`. Page sizes are 1–100. Cursors bind to the company, generation, relationship and filters. Basic field comparisons, related-record filters and bounded `and`/`or` groups are supported. Use absolute timestamps and `orderBy` with `createdAt` or `updatedAt`; custom sort expressions and relative durations are excluded. Archived and trashed issues/projects are hidden from default lists; `includeArchived: true` includes them. Delete trashes these records and unarchive restores them. Explicit permanent issue deletion removes its Linear projection. The schema is a supported subset of Linear's published contract. Team, user and status configuration, rich document state, cycles, roadmaps, initiatives, releases, external integrations, reactions, attachments, subscriptions, notifications, webhooks and automation are excluded. These workflows remain inside the generated company and do not send requests or notifications to Linear. ## Execute routes Chase, Monday, Sage Intacct, ClickUp, DocuSign, Dropbox and OneDrive expose both execute and native APIs backed by the same project records. FCI provides derived financial summaries through execute and retains preview status. ```http POST /api/v1/orgs/{company_slug}/execute X-API-Key: YOUR_RUNTIME_KEY Content-Type: application/json { "org_id": "YOUR_COMPANY_PROJECT_ID", "user_id": "your-integration", "turn_id": "unique-operation-id", "capability_key": "sage_intacct.customers.list", "args": {} } ``` http The legacy `org_id` field identifies the generated **company project**, not the workspace organization. Check the response's `status`: execute errors may use HTTP 200 with `status: "error"`. Reuse `turn_id` when retrying an operation. | Provider | Workflow covered by generated-company verification | | ----------------- | --------------------------------------------------------------------------------------------------------- | | Chase | Read accounts; create a synthetic ACH/wire payment; retrieve it and its changed balance | | ClickUp | Read generated teams/lists; create, update and retrieve tasks and comments | | DocuSign | Create a synthetic envelope; retrieve documents and recipients; send and void it; create from a template | | Dropbox, OneDrive | Create folders; upload, list and download the same binary content | | FCI | Read financial summaries derived from the generated company | | Monday | Create and update items; preserve board/group membership and comments | | Sage Intacct | Manage customers/vendors and GL accounts; create and post bills/journals; allocate payments and move cash | File content uses base64. File paths are scoped to the company and provider. Missing files, tasks, envelopes and payments return an error. Synthetic DocuSign state changes do not send an email, and synthetic Chase payments do not move money through a bank. Future-dated Chase execution is unsupported. ## Export packs Download a ZIP from `/api/v1/orgs/{company_slug}/export-pack/{target}`. The seven targets are `qbo`, `hubspot`, `asana`, `chargebee`, `pipedrive`, `salesforce` and `zendesk`. These contain generated company records in the documented spreadsheet layouts. Some captured pack members intentionally contain headers only; consult each pack's schema for supported artifacts. ## Coverage and fidelity The workflows here describe supported operations, request forms, and observable state changes. Additional vendor endpoints need their own compatibility verification. Inspect the [capability metadata](/api-reference/capability-matrix) and test the exact methods your software uses. Native-shaped responses remain approximate; they are not a certification of full upstream API parity. ### Paddle Billing Use provider ID `paddle` with `Paddle-Version: 1` (also the default). Manage customers, their addresses/businesses, products and prices through Paddle's usual POST, GET and PATCH paths. Archive catalog records with `status: archived`; list them explicitly with `status=archived`. Lists use `per_page`, `after` and `meta.pagination.next`, scoped to the company. For a manual invoice, create a product and price, then POST `/transactions` with `collection_mode: manual`, `items: [{price_id, quantity}]` and `billing_details.payment_terms: {interval: day, frequency: 30}`. Add the `customer_id` and a matching `address_id` to make it ready, then PATCH its `status` to `billed`. Billing creates an invoice visible through the canonical accounting records. Recurring prices also create a subscription; retrieve its ID from the billed transaction. There is no POST `/subscriptions` endpoint. Subscriptions support immediate pause/cancel with `effective_from: immediately`. Resume before the previous billing period ends with `on_resume: continue_existing_billing_period`. Scheduled changes, new renewal invoices and proration are excluded. Subscription customer/address/business, billing details and custom data can be updated. Billed transactions permit custom-data edits only; draft/ready transactions may be canceled. Catalog prices support USD, EUR and GBP. Manual financial writes support USD, zero-tax fixtures without discounts, and positive totals up to `999999999999` cents. Billing does not collect money or post a general-ledger journal. Automatic payments, checkout, hosted portals, tax calculation, adjustments/refunds, trials, reports and webhooks are outside this contract. Portal URLs are synthetic metadata, not functioning hosted pages. The official JavaScript `@paddle/paddle-node-sdk@3.10.0` is tested without changing its serialization or response classes. Its `environment` option accepts the Fictix base URL at runtime and `customHeaders` carries the runtime key and idempotency key. The package's TypeScript `Environment` enum does not advertise arbitrary base URLs. No live Paddle calls, payments or notifications are sent. ### Recurly Use provider ID `recurly` and `Accept: application/vnd.recurly.v2021-02-25`. Create accounts with a unique `code` and fixed-price plans with `code`, `name` and `currencies: [{currency: USD, unit_amount: 25}]`. Account codes and plan codes can be fetched with `/accounts/code-{code}` and `/plans/code-{code}`. Accounts map to shared customers; plans map to shared inventory items. Plan updates use PUT and return 201, following the published API specification. POST `/subscriptions` with `plan_code`, `account: {code}`, `currency: USD`, `collection_method: manual` and an optional quantity. This creates the account if needed, the subscription and one canonical invoice in the same transaction. Retrieve invoices through `/subscriptions/{id}/invoices`, `/accounts/{id}/invoices`, `/invoices/{id}` or `/invoices/number-{number}`. Subscription UUID lookup uses `/subscriptions/uuid-{uuid}`. Billing lists contain records created through this Recurly adapter. Manual net terms include the additional 24 hours used by Recurly: `net_terms: 30` becomes due 31 days after invoice creation. Subscription metadata changes affect future settings; they do not rewrite an existing invoice. PUT `/subscriptions/{id}/cancel` with `timeframe: term_end` or `bill_date` to mark the subscription canceled. Reactivate it before its expiry with PUT `/subscriptions/{id}/reactivate`. DELETE `/subscriptions/{id}?refund=none` terminates it immediately and leaves the invoice outstanding. Account deactivation requires all its Recurly subscriptions to be terminated; PUT `/accounts/{id}/reactivate` restores the account. Removed plans remain readable and cannot be used for new subscriptions. Lists use `limit` (1–200), timestamp `sort`, `order`, `begin_time`, `end_time` and scoped cursor URLs in `next`. Account lists support email/subscriber filters; plans, subscriptions and invoices support their documented state filters. The `ids` filter returns at most 200 IDs and cannot be combined with pagination or other filters. Unsupported parameters return validation errors. Plans support USD/EUR/GBP, fixed prices with at most two decimal places, day/month intervals, no setup fee and no trial. Financial writes support USD totals from 0.01 to 9,999,999,999.99 with zero tax. Renewal flags and term dates are stored; automatic renewals and scheduled expiry do not execute. Proration, pauses, payment collection/refunds, purchases, coupons, add-ons, tax calculation, general-ledger posting, webhooks and HEAD list counts are outside this contract. The official `recurly@4.79.0` Node SDK is tested with its serializers, resource classes, errors and pagination unchanged. It has no public base URL option, so the test routes its HTTPS transport to a loopback bridge. Public request headers carry `X-API-Key` and `Idempotency-Key`; applications using this SDK need a transport override or proxy for the Fictix endpoint. No live Recurly traffic or payment collection occurs. ### PayPal Use provider ID `paypal`. Create catalog products at `/v1/catalogs/products` with `name` and `type` (`PHYSICAL`, `DIGITAL` or `SERVICE`). Products map to shared inventory items. Their published PATCH operations support add, replace and remove for description, category, image URL and home URL. Product name and type are immutable through PATCH. Custom product IDs must be 6–50 URL-safe letters, digits or `._~-`; omitted IDs receive the `PROD-` prefix. Product lists use page numbers, `page_size` up to 20 and optional `total_required=true`. Search generated payment history through `/v1/reporting/transactions` with absolute `start_date` and `end_date` timestamps spanning at most 31 days. Page sizes can reach 500. Supported filters are exact transaction ID, T-code, status and currency. Select `fields=transaction_info`, `payer_info` or `all`. Captured-payment records come from positive canonical customer payments; retrieve a report's transaction ID at `/v2/payments/captures/{id}`. POST `/v2/payments/captures/{id}/refund` with an empty object to request the remaining captured amount, or `amount: {currency_code: USD, value: "0.50"}` for a partial refund. Read it at `/v2/payments/refunds/{id}`. Requests remain `PENDING` and reserve that amount against further requests. Concurrent requests cannot reserve more than the capture. Pending refunds appear in transaction searches with `balance_affecting_records_only=N`, status `P` and event `T1107`. They do not change the original payment, invoice, cash or general ledger. Writes accept `PayPal-Request-Id` (1–38 characters) or Fictix `Idempotency-Key`; both headers must agree when supplied together. Responses use full representation, including with `Prefer: return=representation`. Minimal representation, refund settlement, checkout/orders, authorizations, vault, billing plans/subscriptions, payouts, balance reports, disputes, mock-response headers, delegated merchant assertions and webhooks are outside this contract. The official `@paypal/paypal-server-sdk@2.5.0` test uses configured Axios headers to carry `X-API-Key` and its `unstable_httpClientOptions` adapter hook to route requests into the local bridge. A local OAuth token prevents vendor token fetches; Fictix authenticates the runtime key. Generated request and response schemas, headers and error handling remain unchanged. This SDK does not expose a general base URL option; applications need a transport override or proxy for Fictix. No PayPal traffic or real refund is sent. ## Adyen Checkout workflows Use `/api/v1/orgs/{slug}/native/adyen/v72` with your Fictix runtime key in `X-API-Key` and `merchantAccount: "FictixDemo"`. Each company has its own merchant data. Writes require `idempotency-key` of at most 64 characters; replays return the original response and conflicting bodies return error 704. `POST /paymentMethods` is a read operation and works with a read-only key. Checkout v72 supports Visa and Mastercard method discovery, allow/block lists, shopper token creation/listing/deletion, manual authorization, and asynchronous capture, cancellation and refund requests. Tokens are scoped to their shopper reference, support the three published recurring processing models, and can also be stored during authorization. Repeated token creation for the same shopper, card and expiry returns the existing token. Deleting a token prevents reuse. For test-card input, use `paymentMethod.type: "scheme"`, `encryptedCardNumber: "test_4111111111111111"` for Visa or `"test_5555555555554444"` for Mastercard, `encryptedExpiryMonth: "test_03"`, `encryptedExpiryYear: "test_2030"`, and `encryptedSecurityCode: "test_737"`. Valid future month/year combinations are accepted. These are Adyen's documented synthetic test credentials; the adapter does not decrypt cards. Authorization requires a positive integer USD amount in cents, a reference of at most 80 characters, an absolute HTTP(S) return URL, and `additionalData: {"manualCapture": "true"}`. It returns `Authorised` and keeps the payment separate from settled customer payments. Captures and cancellations apply to these open native authorizations. Refunds apply to existing posted or settled canonical customer payments. Modification responses return `received`; amounts are reserved so competing requests cannot over-capture or over-refund. A pending capture is not eligible for a refund. Pending cancellation prevents further capture. Cash balances, invoice balances and journal entries remain unchanged until settlement, which this workflow does not implement. The official `@adyen/api-library` 32.1.0 SDK is exercised against these routes, including its generated serializers, authentication, token methods, payment methods, modification responses and parsed errors. The SDK has no public Checkout base-URL setting; route its HTTP transport to the company native prefix while retaining the `/v72/...` path. The checked-in SDK test redirects only the socket destination to a local HTTP bridge and prohibits upstream traffic. Automatic/delayed capture, authorization expiry, refund settlement, declines, 3DS, sessions, hosted checkout and payment links, other payment methods, splits, balance-platform operations and webhooks are outside this contract. ## QuickBooks query and amount behavior The QBO query route supports `SELECT * FROM `, optional single equality filters, `STARTPOSITION`, and `MAXRESULTS` (1–1000). All entities support `Id`; invoices support `DocNumber`/`CustomerRef`, bills and bill payments `VendorRef`, payments `CustomerRef`, customers and vendors `DisplayName`, accounts and items `Name`, and journal entries `DocNumber`. Filters use exact string values and backslash escaping for apostrophes. Filtering happens before pagination. Unsupported operators, compound predicates, sorting, and extra statements return a QBO fault. Invoice and bill totals use the shared accounting total; money fields are JSON numbers, including zero balances. ## FreshBooks accounting workflows Use `/api/v1/orgs/{slug}/native/freshbooks` with `X-API-Key`, `Api-Version: alpha` (the default), and `Idempotency-Key` on writes. Identity discovery at `GET /auth/api/v1/users/me` returns the company-local `FICTIX` account. Accounting routes start with `/accounting/account/FICTIX`: `/users/clients`, `/items/items`, `/invoices/invoices`, and `/payments/payments`. Clients, items and invoices support POST, list/single GET, and PUT by ID. Payment allocations support list/single GET. Bodies use the singular resource wrapper, such as `{"client":{"fname":"Ada","lname":"Lovelace"}}`. Create an invoice with an active `customerid`, `create_date` in `YYYY-MM-DD` format, and ordinary `lines` containing `name`, decimal `qty`, and `unit_cost: {"amount":"12.50","code":"USD"}`. Up to 100 lines are accepted; each line is rounded to cents before totals are summed. Omitted invoice numbers are allocated uniquely within the company. `due_offset_days` supports 0–3650. PUT replaces the complete line array when supplied; omitted fields are retained. Invoices start as drafts outside the shared invoice collection. PUT `{"invoice":{"action_mark_as_sent":true}}` issues the invoice with the same native ID and exposes it to other integrations. This action sends no email. Only invoices created through this adapter can be updated; paid and partially paid invoices cannot be changed. Invoice and item price writes support USD. Client currency preferences also support EUR and GBP. Payment reads expose one row per existing invoice allocation, preserving its amount and invoice/customer links; unapplied balances are omitted. Lists support `page`, `per_page`, documented scalar search fields, and supported sort fields. Page zero becomes page one; page size defaults to 15 and is capped at 100. PUT `vis_state: 1` deletes a resource from normal reads, `2` archives it, and `0` restores it. The delete response contains the updated resource, matching the official SDK fixtures. Unsupported parameters return an error. The official `@freshbooks/api` 4.1.0 SDK is exercised through its public `apiUrl` setting and an Axios transport adapter that adds the Fictix runtime key and idempotency headers. Applications using this SDK need those transport headers; its OAuth bearer token alone does not authenticate with Fictix. SDK request and response conversion, money/date parsing, pagination and errors are preserved. Tax and discount calculation, automatic billing, payment collection, journal posting, invoice email, credits, expenses, projects, time tracking, reports, attachments, secondary contacts and webhooks are outside this workflow. ## Zoho Books accounting workflows Use `/api/v1/orgs/{slug}/native/zoho_books/books/v3` with your Fictix runtime key in `X-API-Key` and `Idempotency-Key` on writes. `GET /organizations` returns the company-local organization ID `100000000000001` and USD currency ID `100000000000002`. Every subsequent request requires `?organization_id=100000000000001`; another organization ID is rejected. `/contacts` supports customers and vendors, addresses and up to ten contact people with one primary contact. POST requires `contact_name`; PUT by contact ID requires `contact_name` and `contact_type`. The contact type cannot change. `/items` supports sales items with a name and numeric USD rate. POST and PUT require `name` and `rate`; a zero rate is valid. Contacts and items support GET by ID, list GET, and POST `/{id}/active` or `/{id}/inactive`. POST `/invoices` creates a draft with an active `customer_id` and `line_items` that reference active `item_id` values. Lines accept positive decimal quantity and an optional numeric rate; amounts are rounded to cents per line. Invoice totals must be positive. PUT by invoice ID replaces the complete line array and requires the customer ID again. Omitted invoice numbers are allocated uniquely. Dates use `YYYY-MM-DD`; payment terms support up to 3650 days. Invoice and item price writes use USD, without tax, discounts, shipping, adjustments or FX. POST `/invoices/{id}/status/sent` issues the draft into the shared invoice collection with the same native ID. The action sends no email. Issued invoices are immutable through this adapter. DELETE supports only drafts created through this adapter. Native-created contacts and items can be deleted when no financial transaction references them. A deleted draft releases those references. `/customerpayments` supports list and single reads of settled payments and their invoice allocations. Pending payments are omitted. Lists support documented scalar filters, supported status filters, `sort_column`, and stable ascending order. `page` starts at one; `per_page` defaults to 10 and accepts 1–200. Unsupported parameters return an error. JSON response contracts are checked against the published OpenAPI modules; invoice totals and balances follow the numeric wire examples where the schema's string annotation disagrees. Payment collection, journal posting, invoice email, recurring invoices, estimates, sales orders, purchasing, inventory movements, credit notes, vendor payments, reports, attachments, custom fields, portals, regional tax settings and webhooks are outside this contract. The workflow makes no live Zoho requests. ## Klaviyo profiles, lists and events Use `/api/v1/orgs/{slug}/native/klaviyo` as the SDK base URL, with a Fictix API key and `revision: 2026-07-15`. Writes require `Idempotency-Key`. JSON responses use `application/vnd.api+json`. These workflows are checked against Klaviyo's published [OpenAPI category specifications](https://github.com/klaviyo/openapi) and the official `klaviyo-api` Node SDK 23.0.0. * Profiles: list/read, create, update and synchronous `POST /api/profile-import` upsert. Email, phone and external ID resolve existing profiles; conflicting identifiers return 409. Names, email and phone remain linked to canonical customers. Custom properties and location fields merge; null clears submitted fields. At least one identifier must remain. * Lists: create/read/update/delete, read member profiles or their relationship IDs, and add/remove up to 1,000 profiles atomically. Repeated additions preserve the original join time. Deleting a list preserves its profiles. `additional-fields[list]=profile_count` reports current membership. Membership changes do not grant marketing consent. * Events: `POST /api/events` persists a submitted event and its profile/metric relationships, then returns an empty 202. Accepted synthetic events are immediately available through event list/read endpoints. The first event with a given profile, metric and `unique_id` is retained; when omitted, the event time to the second is the deduplication key. Optional `value` is represented as `$value` in event properties; it has no financial effect. * Metrics: list/read API metrics created by submitted events. No engagement, purchase or campaign metrics are fabricated from unrelated financial records. Profile and list reads support their published basic identifier/date filters. Event reads support profile ID, metric ID, datetime and timestamp comparisons. List-member reads support email, phone and join-date filters; metrics support integration name/category equality. Supported predicates compose with `and`, `or`, `not` or comma-separated conjunctions, bounded to 16 expressions and four nested levels. String literals use quotes; `any` accepts a string array. Unsupported fields/operators fail explicitly. Sorting is limited to the published fields implemented for each endpoint. Pagination honors profile/member limits of 1–100, list limits of 1–10, event limits of 1–1,000 and a fixed 200-metric page. Cursors bind to the company, incarnation, endpoint, query and ordered record IDs; insertions, removals or reordering require starting again. Next/previous links use the same Fictix native base URL. Unavailable links are omitted. Sparse fieldsets support implemented attributes and nested location fields. Event reads can include `profile,metric`; individual profile reads can include `lists`. Campaign delivery, email/SMS subscriptions, consent administration, SMS provisioning, attribution, predictive analytics, segments, flows, bulk jobs, profile merge/deletion, profile meta patches and `value_currency` are outside this workflow. Unsupported versions, requests and query parameters return errors. E.164 phone syntax is validated; upstream account-specific SMS rules are not simulated. No messages are delivered and no money or ledger entries change. ## Mailchimp audiences, members and tags Use `/api/v1/orgs/{slug}/native/mailchimp/3.0` as the Marketing SDK base URL, with a Fictix API key. Writes require `Idempotency-Key`. These workflows are checked against Mailchimp's published [Marketing API specification](https://github.com/mailchimp/mailchimp-client-lib-codegen/blob/main/spec/marketing.json) 3.0.91 and the official `@mailchimp/mailchimp_marketing` Node SDK 3.0.80. * Audiences: list/read, create, update and delete through `/lists`. Creation requires the published audience settings, contact and campaign defaults. Updates require the complete settings payload, including contact state and postal code. Deletion preserves canonical customers. * Members: list/read, create, update, `PUT` upsert and archive through `/lists/{list_id}/members`. The subscriber hash is MD5 of the lowercase email address. Email changes update that identifier. Names, email and phone remain linked to canonical customers. An archived member is excluded from ordinary reads; `status=archived` lists archived records, and upsert can restore them. * Tags: list a member's tags, add/remove named tags atomically, and search audience tags. Repeated additions preserve the original assignment time. * Merge fields: read the fixed `FNAME`, `LNAME` and `PHONE` definitions. These values update the corresponding canonical customer fields. The initial audience `f1c71ca001` derives its contacts from canonical customers with email addresses. Those initial contacts have `transactional` status; no marketing consent is inferred. Its sender and postal address are explicitly synthetic placeholders. Custom audiences contain only explicitly added members. Archiving preserves the canonical customer, and deleting the initial audience does not silently recreate it. Audience counts reflect current member statuses; campaign counts are zero because campaign activity is not simulated. Lists support email/date filters and creation-date sorting. Members support status, email type, unique email ID, VIP and timestamp filters, with sorting on the implemented opt-in, signup and change timestamps. Collection reads accept `count` from 1 to 1,000 and a nonnegative `offset`. `fields` and `exclude_fields` select response fields and cannot be combined. Unsupported query parameters and write fields fail explicitly. Errors use `application/problem+json`. Campaign creation or delivery, engagement reporting, automations, commerce, custom merge-field administration, interests, marketing permissions, notes, SMS, bulk jobs and permanent contact deletion are outside this contract. Submitted subscription statuses and campaign defaults are synthetic state; they do not deliver messages or change a live Mailchimp account. ## Deel personnel and organization structures Use `/api/v1/orgs/{slug}/native/deel/rest` as the base URL, with a Fictix API key. Writes require `Idempotency-Key`. These workflows use Deel's published [OpenAPI specification](https://api.letsdeel.com/openapi/rest/definitions) 1.25.0 and its operations dated 2026-01-01. `GET /people` and `GET /people/{hris_profile_id}` expose canonical personnel, stable UUIDs, names, work and personal emails, job titles, start dates, departments and manager/report relationships. Active and future start dates determine personnel status. Employment-contract arrays are empty because this workflow does not create Deel contracts. `GET /organizations` returns the current synthetic organization. People lists support name search, supported name/identifier/date/job/status sorts, repeated `hiring_types`, `hiring_statuses` and `person_statuses` query parameters, and repeated `fields` parameters for implemented top-level fields. Pages use `offset` and `limit`, defaulting to 100 records with a maximum of 200. The `page` object reports the filtered total and requested page size. Unsupported selectors, filters and relationship expansions return errors. `/hris/organization_structures` supports list/create/read/update/delete, with individual lookup, update and deletion also available through `/hris/organization_structures/external/{external_id}`. Structures hold named teams and optional external IDs. Creation uses `is_multiple_select`, its response uses `is_multi_select`, and later reads/updates use `is_multiselect`, following the published contracts. Updates require `teams`; existing team IDs preserve identity, and omitted teams are removed. Each structure supports up to 200 flat teams. PATCH and DELETE acknowledge success with an empty 200. The initial Departments structure reflects canonical department assignments. Renaming a department team updates the corresponding personnel records. Removing an assigned department or its structure fails atomically. Custom structures do not implicitly assign staff or modify payroll. Person creation, people without contracts, contract hiring/signing, invitations, payroll, benefits, payments, worker assignment, custom fields, roles, team hierarchies and webhooks are outside this workflow. No live Deel calls occur. ## Meta Ads account and campaigns Use `/native/meta_ads/v26.0` with a Fictix runtime key. Discover the synthetic account with `GET me/adaccounts`, or read `GET act_1000000001?fields=id,account_id,name,currency,timezone_name`. The account uses USD and UTC. Campaigns start empty. POST `act_1000000001/campaigns` with JSON or the official SDK's URL-encoded form: ```json { "name": "Website traffic test", "objective": "OUTCOME_TRAFFIC", "special_ad_categories": [], "daily_budget": 1000, "status": "PAUSED" } ``` json The response supplies `id`. Read `GET {id}?fields=name,status,daily_budget` to verify persisted values. The daily budget is in currency minor units: `1000` represents USD 10.00 and is returned as the string `"1000"`. This workflow supports auction campaigns, `OUTCOME_TRAFFIC` or `OUTCOME_AWARENESS`, daily campaign budgets, `LOWEST_COST_WITHOUT_CAP` bidding, no special ad categories and no ad-set budget sharing. New campaigns can be `PAUSED` or `ACTIVE`. POST `{id}` to update supported campaign fields; objective changes are rejected. Updates return `{"success": true}` after persistence. Set `status` to `ARCHIVED` to archive a campaign, or use `DELETE {id}` to delete it. Archived and deleted campaigns cannot be edited; deletion remains available after archive. The runtime retains their status for inspection. Default campaign lists include `ACTIVE` and `PAUSED`; use the JSON-encoded `effective_status` query parameter to request `ARCHIVED` or `DELETED` records explicitly. `GET act_1000000001/campaigns` accepts comma-separated `fields`, `limit` up to 100, `before` or `after`, and `summary=true`. Cursors and next/previous URLs are bound to the company, incarnation, query and matching campaign state. Restart paging if that state changes. Writes need the `write` scope and use Fictix's shared idempotency ledger; field selection on writes is limited to `id` on creation. Read other fields with a separate GET. The official Python `facebook-business` 26.0.1 SDK can use the documented `FacebookSession.GRAPH` base URL and `requests` session attributes to target this runtime and send `X-API-Key`. No live vendor token is required. This bounded workflow does not create ad sets, ads, creatives or audiences, serve ads, spend money, produce insights, implement targeting, special categories, lifetime budgets, async/batch Graph requests or OAuth. ## Google Analytics 4 events and reports Use `/native/google_analytics_4` with a Fictix runtime key. The company's synthetic property is `properties/1000000001`, its web measurement ID is `G-FICTIX001`, and its reporting time zone is UTC. No web traffic is inferred from financial or personnel records: reports begin empty and use only events collected into that company's runtime. `GET v1beta/properties/1000000001/metadata` lists the supported dimensions and metrics. `POST v1beta/properties/1000000001:checkCompatibility` checks requested fields. Supported dimensions are `date`, `eventName`, `pagePath`, `pageLocation`, `hostName`, `pageTitle` and `platform`; metrics are `eventCount`, `totalUsers` and `screenPageViews`. User counts use distinct web client instances, including normalization of the documented GA client-cookie form. Send a web Measurement Protocol payload to `POST debug/mp/collect?measurement_id=G-FICTIX001` to validate without collecting: ```json { "client_id": "123456789.1234567890", "events": [{ "name": "page_view", "params": {"page_location": "https://sample.test/pricing", "page_title": "Pricing"} }] } ``` json Valid requests return `{"validationMessages": []}`. To persist the same payload, use `POST mp/collect?measurement_id=G-FICTIX001`, which returns empty `204` only after the complete batch is saved. Batches accept up to 25 events with up to 25 scalar parameters each. Use a Fictix idempotency key to replay a write safely. Fictix authentication uses `X-API-Key`; the vendor `api_secret` query parameter is optional and is not used to authenticate this synthetic interface. POST `v1beta/properties/1000000001:runReport` to read the saved events: ```json { "dateRanges": [{"startDate": "7daysAgo", "endDate": "today"}], "dimensions": [{"name": "pagePath"}], "metrics": [{"name": "screenPageViews"}, {"name": "totalUsers"}], "orderBys": [{"metric": {"metricName": "screenPageViews"}, "desc": true}], "limit": "100" } ``` json One date range is supported, with absolute dates or `NdaysAgo`, `today` and `yesterday`. Use `dimensionFilter` with exact, begins-with, ends-with, contains or in-list matches; bounded AND, OR and NOT groups are supported. Ordering uses requested columns, and `offset`/`limit` are int64 decimal strings. `rowCount` describes all matching groups before pagination. `batchRunReports` accepts up to five requests. All report, compatibility and debug-validation POSTs require only `read`; collection requires `write`. Counts are available immediately in this runtime, so a separate report can verify collection. This is a bounded web workflow. Invalid or unsupported collection fails with HTTP `400` and makes no changes; Google's upstream collector can instead return `2xx` and discard invalid events. Fictix always enforces the preceding 72-hour timestamp window, even with `RELAXED` validation. App streams, User-ID identity, geolocation, consent and advertising attribution, session/engagement/revenue metrics, custom definitions, realtime, pivots, cohorts, metric filters, quota simulation and OAuth are outside this scope. Metadata advertises only the implemented fields. The official `google-api-python-client` 2.200.0 REST discovery client is verified for metadata, reports, batch reports and compatibility using the published v1beta Discovery document and its public endpoint/HTTP transport options. ## Google Ads accounts, budgets and Search campaigns Use `/native/google_ads/v25` with a Fictix runtime key. Start with `GET customers:listAccessibleCustomers`; the generated company owns the synthetic account `customers/1000000001`, with USD currency, UTC time zone and `testAccount: true`. Campaigns and budgets start empty. POST `customers/1000000001/campaignBudgets:mutate` with an `operations` array. A budget `create` accepts `name`, a positive int64 decimal string `amountMicros`, and shared daily standard-delivery settings. Then POST `campaigns:mutate` under the same customer with a campaign `create` containing `name`, `campaignBudget`, `advertisingChannelType: "SEARCH"`, `manualCpc: {}`, an explicit `containsEuPoliticalAdvertising` enum and optional `status: "PAUSED"` or `"ENABLED"`. The response returns resource names; `responseContentType: "MUTABLE_RESOURCE"` also returns mutable fields. Each batch of up to 100 operations is atomic. `validateOnly: true` checks the batch without persisting it. POST `googleAds:search` or `googleAds:searchStream` with a GAQL `query`, for example: ```sql SELECT campaign.id, campaign.name, campaign.status, campaign_budget.amount_micros FROM campaign WHERE campaign.status != 'REMOVED' ORDER BY campaign.id ``` sql These POST endpoints need only the `read` scope. Supported resources are `customer`, `campaign` and `campaign_budget`, including related account/budget fields. Queries support explicit fields, one `=` or `!=` predicate, one ordering and a positive `LIMIT`. Search uses v25's fixed 10,000-row pages, opaque `nextPageToken`, and optional `searchSettings.returnTotalResultsCount` or `omitResults`. `pageSize` is rejected. Page tokens are bound to the company, incarnation, query, settings and matching state. SearchStream returns the published JSON array envelope in one response. Summary rows are unsupported. Update with an operation containing `update` and an exact camel-case `updateMask`. Budget updates accept `name` and `amountMicros`; campaign updates accept `name`, `status`, `campaignBudget` and `containsEuPoliticalAdvertising`. A `remove` operation marks a resource `REMOVED`; a budget cannot be removed while a non-removed campaign references it. Independently query the resource to verify every mutation, and use the same idempotency key for a retried write. This is a bounded REST interface. It does not claim Google Ads gRPC SDK support, serve ads, bill an account, fabricate spend or traffic, implement metrics, segments, ad groups/ads, temporary IDs, partial failures, manager delegation, OAuth, other campaign types or every upstream error detail. Unimplemented fields and operations return explicit errors. The official `google-api-python-client` 2.200.0 REST discovery client is verified against these routes using the published v25 document, its public `client_options.api_endpoint` override and a Fictix-authenticated HTTP transport. ## Justworks company and personnel Use `/native/justworks/v1` for company/member reads and job, department and manager updates. `GET /company` supplies the department IDs used in `PATCH /members/{member_id}`. The PATCH body accepts `job_title`, `department_id` and `manager_id`; success returns empty HTTP 204 after the canonical employee record is saved. References must exist in the same company. Self-management and reporting cycles return HTTP 422 without partial changes. Names, job titles, department and manager relationships remain consistent with the other HR providers. Existing department IDs survive moves out of a department. Member lists return `items` and an optional `next_cursor`. Paging defaults to 100 rows and accepts `limit` up to 100. Filters include `status=active` or `status=terminated`, plus `updated_at` and its `_gt`, `_gte`, `_lt`, `_lte` and `_ne` date variants. Cursors are bound to the company incarnation, query and roster state; restart paging after changing the roster. IDs are stable within the company. Absent source creation/update times use the synthetic `2000-01-01T00:00:00Z` baseline. The implementation is checked against the OpenAPI 1.32.5 document embedded in [Justworks' published API reference](https://public-api.justworks.com/v1/docs). It covers legal/preferred names, work emails, active status and known employment dates. It does not implement member creation or onboarding, payroll, paystubs, deductions, compensation, sensitive identity fields, benefits, offices, custom fields, OAuth flows or webhook delivery. Job and reporting-line updates have no payroll or payment effects. ## SAP business partners Use `/native/sap/sap/opu/odata/sap/API_BUSINESS_PARTNER` as the OData v2 base. `A_BusinessPartner`, `A_Customer` and `A_Supplier` support JSON list and single reads with SAP `d`/`results` envelopes. Generated customers and vendors have stable master-data IDs and matching customer or supplier references. The partner's `to_Customer` and `to_Supplier` navigation properties resolve those existing roles and can be expanded. Names reflect canonical records. Create a standalone organization with `POST A_BusinessPartner` and `{"BusinessPartnerCategory":"2","OrganizationBPName1":"Example business"}`. Category `1` creates a person and requires `LastName`; `FirstName` is optional. The implemented grouping is the synthetic `0001`. Creation returns HTTP 201, the assigned `BusinessPartner` and a `Location` header. Use `PATCH A_BusinessPartner('ID')` for name, search-term and language updates; success returns empty HTTP 204. An optional `If-Match` header checks the ETag returned by GET and rejects stale updates with HTTP 412. Fictix idempotency prevents duplicate creates. New partners have no customer or supplier role; these writes do not create financial transactions. Queries support `$top` up to 1000, `$skip`, `$inlinecount=allpages`, `$count`, supported-property `$select`, one string `eq` or `ne` predicate and one `$orderby` property. Default pages contain up to 100 records and include `__next` when more remain; explicit `$top` limits the total returned rows. Only JSON is supported. Unsupported query syntax fails explicitly. The workflow follows [SAP's published Business Partner metadata](https://github.com/SAP-samples/cloud-cap-samples-java/blob/main/srv/external/API_BUSINESS_PARTNER.edmx). SAP Cloud SDK clients can use public `setBasePath`, `addCustomHeaders` and `skipCsrfTokenFetching` options with a Fictix API key. SAP session cookies and CSRF token negotiation are unavailable. This subset does not implement deletion, deep inserts, role creation, addresses, tax or bank information, company codes, sales or purchasing areas, batch, metadata/XML, SOAP, payroll, invoices or other ERP APIs. Existing customer/vendor name updates remain visible through other native providers without changing their ledger records. ## Workday workers and check-ins Use `/native/workday/staffing/v7` as the base for the implemented Staffing v7 REST workflows. Worker list and single reads expose canonical worker names, employee IDs, work contacts and business titles. Worker WIDs are stable; single reads also accept `Employee_ID=...`. Lists default to 20 rows, accept `limit` up to 100 and `offset`, and exclude terminated employees unless `includeTerminatedWorkers=true`. Case-insensitive `search` matches name or worker ID, with space-separated terms combined using OR. For a returned worker ID, `/workers/{ID}/checkIns` and `/workers/{ID}/checkInTopics` support create, list, read, partial update and delete. Creation and ordinary PATCH requests use multipart form data containing one `jsonData` part with `Content-Type: application/json`. For example, the check-in JSON part is `{"date":"2026-09-06","description":"Weekly review"}`; a topic requires `name`. The response is JSON. Check-ins can reference existing topics through `associatedTopics: [{"id":"..."}]`; topics can reference check-ins through `associatedCheckIns`. Reads resolve both sides of those links from persisted records. DELETE removes the resource and its links and returns HTTP 200 with an empty `data` collection. Archive and restore use `PATCH .../{subresourceID}?type=archive` with an ordinary JSON body `{"archive":true}` or `{"archive":false}`. Only the creating Fictix credential can edit, archive or delete a check-in or topic. `privateNotes` are returned only to that credential and are omitted from generic record materialization. This workflow does not associate credentials with employees, so `me` and editing as a participant are unavailable. Standard Fictix idempotency applies to normalized multipart JSON, independent of its boundary. The implementation follows the [published Staffing v7 OpenAPI specification](https://developer.workday.com/bundles/rest-directory-ui/public/static/openApiFiles//staffing_v7_20260822_oas3.json) for these operations. It does not implement attachment uploads, other subresource reference IDs, supervisory visibility, metadata responses, tenant-prefixed paths, SOAP, hiring, payroll, compensation or job changes. Check-in writes do not alter employment or financial records. Unsupported fields and query options return errors. ## ADP Workforce Now workers and legal names Use `/api/v1/orgs/{slug}/native/adp` as the API base URL, with a Fictix API key. Writes require `Idempotency-Key`. This workflow follows ADP's published [Workforce Now guide](https://developers.adp.com/articles/preview/guide-worker-management-api-guide-for-adp-workforce-now-0) and [official request/response samples](https://github.com/adpllc/marketplace-sample-payloads/tree/master/wfn). These are documentation and wire-example checks; an official SDK or complete OpenAPI conformance is not claimed. `GET /hr/v2/workers` and `GET /hr/v2/worker-demographics`, including individual `/{associateOID}` reads, return canonical employee names, work email, job title, hire dates, department and manager references, and active/terminated status. Each worker has distinct stable associate, employee and position identifiers. Sensitive demographic fields, compensation and payroll histories are omitted. Lists support `$top` from 1 to 100, defaulting to 50, and nonnegative `$skip`. Reads after the last record return an empty 204. `$select` supports the implemented nested worker fields. A single `$filter` equality can select by worker ID, position ID, manager position ID, assignment status, or department code/type. `count=true` on `/workers` reports the unfiltered population in `meta.totalNumber`, following ADP's documented limitation. The only supported role is `Practitioner`. Unsupported queries, historical dates, asynchronous preferences and sorting options return errors. `POST /events/hr/v1/worker.legal-name.change` accepts one event containing `data.eventContext.worker.associateOID` and `data.transform.worker.person.legalName`. It updates `givenName`, `familyName1`, `middleName` and `nickName`. The completed event response is returned only after the canonical employee change persists. Subsequent ADP and other personnel provider reads reflect the change; retries with the same key replay the stored response. Invalid fields or unknown workers leave state unchanged. Onboarding, termination, scheduled changes, payroll, compensation, addresses, government identifiers, birth dates, code tables, photos, metadata endpoints and event-notification delivery are outside this workflow. No live ADP requests or financial changes occur. ## TriNet HR Platform The `trinet` native base implements the public HR Platform Core API, formerly Zenefits, for reading a generated company's personnel and organization. This is separate from TriNet PEO. Use your Fictix `X-API-Key` and replace the upstream `https://api.trinet.com` base with your company's `/native/trinet` base. Start with `GET core/companies` or `GET core/people`. The response contains `data.data`, `data.next_url`, `object: "/meta/response"`, numeric `status: 200` and the published string `error: "null"`. A detail request such as `GET core/people/{id}` contains the person directly in `data`. Follow returned company, department, manager, subordinate and employment URLs within your Fictix company. IDs are numeric strings; a person's API `id` is distinct from `employee_number`. Supported routes are company, people, department and employment lists/details, company people/departments, department people, and person subordinates/employments. Lists default to 20 rows, accept `limit` up to 100, and use exclusive `starting_after` or `ending_before` object IDs. Follow `next_url` for subsequent pages. Pagination IDs must belong to the current filtered collection; a roster change can invalidate one. Results are ordered by numeric API ID. People support exact `company`, `department`, `manager`, `first_name`, `last_name` and `status` filters. Company lists accept `name`; departments accept `company` and `name`; employments accept `person`. Unknown filters are rejected. `includes` takes space-separated paths, for example `includes=company.departments department.company manager employments.person`. Expandable fields are company `departments`; people `company`, `department`, `manager`, `employments`; department `company`; and employment `person`. Dotted paths may be three levels deep, with at most ten paths and 2,000 rendered records. Expanded lists use the default 20-row page and provide a `next_url`. Other relationships remain references that you can follow separately. These reads reflect canonical personnel changes made through supported HR integrations such as Justworks. Each employee has one current employment projection with known hire/termination dates and employment type when available. No past employment, compensation, bank, tax, address, benefit or demographic facts are invented. Roster members default to active unless canonical status or `active=false` indicates otherwise. Department identity derives from its case-folded canonical label and changes when the label is renamed. PEO, payroll, Core writes, locations, installation/subscription flows, OAuth and webhooks are outside this workflow. Follow the returned `next_url` until it is `null`; the final page follows the published pagination guide. Sources: [Core people](https://developers.zenefits.com/reference/people), [companies](https://developers.zenefits.com/reference/company), [departments](https://developers.zenefits.com/reference/department), [employments](https://developers.zenefits.com/reference/employment), [pagination](https://developers.zenefits.com/reference/pagination), and [inline expansion](https://developers.zenefits.com/reference/inline-expansion). # Rate limits and quotas This page documents the operational limits an API client should design around. Some values are environment-configurable, so client code should read response errors and headers instead of hard-coding every number. ## Request rate limiting The default API rate limit is: ```text 120 requests per 60 seconds ``` text The backend reads these environment variables: | Setting | Default | Meaning | | -------------------------------- | -------------------- | ------------------------------------------------------------- | | `RATE_LIMIT_ENABLED` | `true` | Enables request rate limiting. | | `RATE_LIMIT_REQUESTS_PER_MINUTE` | `120` | Requests allowed per bucket window. | | `RATE_LIMIT_WINDOW_SECONDS` | `60` | Window size in seconds. | | `RATE_LIMIT_BACKEND` | `auto` | Uses Redis when configured, otherwise memory. | | `RATE_LIMIT_REQUIRE_REDIS` | production dependent | Fails startup/readiness if Redis is required but unavailable. | When rate limited, clients receive: ```http 429 Too Many Requests ``` http ![](phosphor/regular/info) Clients should back off using the retry hint returned by the deployment when available. If no header is present, use exponential backoff with jitter. ## Pagination limits Entity browsing uses bounded pagination: GET`/api/v1/demo-jobs/{job_ref}/entities/{entity}` ```text GET /api/v1/demo-jobs/{job_ref}/entities/{entity}?limit=100&offset=0 ``` text Current maximum page size: ```text 1000 records ``` text Clients should page until: ```text offset + data.length >= count ``` text Do not assume that a single page can contain a full ledger. ## Partner bundle limit The normalized bundle endpoint is capped so one response cannot accidentally ship an entire large tenant through a JSON response. Default: ```text PARTNER_BUNDLE_MAX_RECORDS=25000 ``` text Endpoint: GET`/api/v1/demo-jobs/{job_ref}/bundle` ```http GET /api/v1/demo-jobs/{job_ref}/bundle ``` http If the selected entities exceed the cap: ```http 413 Payload Too Large ``` http ```json { "detail": "Bundle too large for one response. Requested 30001 records across 15 entities, which exceeds the partner bundle cap of 25000. Use the entity or export endpoints instead." } ``` json ![](phosphor/regular/warning) The cap can be changed by deployment configuration. Treat `413` as a signal to page or export, not as a numeric limit to hard-code into a client. Recovery options: * request fewer entities with repeated `entity` query params * use paginated entity reads * use per-entity exports * use the runtime CSV export pack route when the use case is "download all data" ## Dataset storage limits The current project dataset blob model has write-time guardrails. Defaults: | Setting | Default | | ------------------------------------ | ----------- | | `FICTIX_MAX_PROJECT_DATASET_RECORDS` | `300000` | | `FICTIX_MAX_PROJECT_DATASET_BYTES` | `134217728` | If a write would exceed the dataset budget, the API returns a structured error: ```json { "detail": { "code": "project_dataset_limit_exceeded", "project_id": "proj_123", "context": "simulation_step", "record_count": 315000, "max_records": 300000, "estimated_bytes": 140000000, "max_bytes": 134217728, "message": "Project dataset exceeds current blob storage safety budget" } } ``` json Client action: * reduce generated scale * clone into smaller scenario-specific jobs * avoid long unbounded simulation runs * export and archive data before destructive reset if needed ## Generation admission limits Fictix estimates generation load before doing expensive work. Default generation budget: ```text MAX_WEIGHTED_GENERATION_LOAD=300000 ``` text Weighted load is roughly: ```text estimated_records * quality_multiplier * connector_multiplier ``` text Quality multipliers: | Quality preset | Multiplier | | -------------- | ---------- | | `clean` | `1.0` | | `realistic` | `1.1` | | `messy` | `1.25` | | `stress_test` | `1.5` | Connector multiplier: ```text 1 + max(0, connector_count - 1) * 0.25 ``` text Full system materialization uses at least 6 connector-equivalent units. If a request is too large: ```http 422 Unprocessable Entity ``` http ```json { "detail": "Requested generation load exceeds the server budget (420,000 weighted units for about 145,000 estimated records, quality 'realistic', 8 connectors; budget 300,000). Reduce employees, months_of_history, customers_per_employee, or connector breadth. With the current shape, try <= 85 employees or <= 12 months_of_history." } ``` json Client action: * lower `generation.employees` * lower `generation.months_of_history` * reduce `customers_per_employee` * select fewer services * use `clean` for broad smoke tests * create multiple smaller demo jobs instead of one huge tenant ## Webhook limits Webhook subscription limits: | Limit | Value | | ------------------------------------- | ----- | | Active subscriptions per org | `20` | | Event filters stored per subscription | `50` | | Delivery worker batch size | `100` | | Synchronous POST attempts | `3` | Webhook delivery is billed/metered as `webhook_deliveries` when quota is configured for the project. ## Plan quotas Plan-level quotas are enforced from the billing catalog. The signed-in Fictix Console is the customer-facing source of truth for an organization's active plan and current usage; do not infer entitlement from a key name or a documentation table alone. Current default catalog limits: | Plan | Test runs/mo | API units/mo | Scenario runs/mo | Events/mo | Storage | Webhook deliveries/mo | | ---------- | ------------ | ------------ | ---------------- | ---------- | ------- | --------------------- | | Starter | 25 | 10,000 | 100 | 100,000 | 500 MB | 0 | | Hobbyist | 100 | 25,000 | 250 | 250,000 | 1 GB | 0 | | Developer | 500 | 100,000 | 5,000 | 2,000,000 | 10 GB | 25,000 | | Pro | 2,500 | 500,000 | 25,000 | 10,000,000 | 50 GB | 100,000 | | Enterprise | 10,000 | 2,500,000 | 100,000 | 50,000,000 | 100 GB | 250,000 | ![](phosphor/regular/warning) When quota is exhausted, clients should stop the workflow or surface an upgrade path. Do not auto-retry quota failures. Use the Console to review usage and upgrade eligibility. ## Client backoff policy Recommended general policy: | Response | Retry? | Policy | | -------- | --------- | -------------------------------------------------------------- | | `429` | Yes | Respect retry hint; otherwise exponential backoff with jitter. | | `500` | Sometimes | Retry idempotent operations only. | | `503` | Yes | Back off and recheck readiness. | | `409` | No | Resolve idempotency conflict or active-operation conflict. | | `413` | No | Page or use exports instead. | | `422` | No | Fix request shape or reduce scale. | POST`/api/v1/demo-jobs` For `POST /api/v1/demo-jobs`, retry only with the same request body and same `Idempotency-Key`. # Changelog ## September 7, 2026 — Provider workflows in staging * The staging catalog reported 60 entries: 59 marked available/selectable and one FCI execute preview, which is not selectable in the standard connector picker. * The first-request directory covers every provider, including GraphQL, XML, form, and execute request bodies, and offers a downloadable JSON reference. * The connection guide covers API keys, SDK base URLs, operation scopes, idempotency, and provider-level errors. The workflow reference documents the supported operations and limits. * Public catalog snapshots observed October 28, 2026 report 60 entries in production as well as staging (59 marked available/selectable and one FCI preview). This is catalog metadata, not production request verification. Relay Financial remains unavailable pending a direct API contract. TriNet refers to HR Platform (formerly Zenefits), not PEO. See [Connect your software](/guides/connect-your-software), [Provider first requests](/api-reference/provider-first-requests), and [Provider workflows](/api-reference/provider-workflows). ## September 5, 2026 — Organization keys in staging * Organization admins can issue one API key for all current and future companies in their organization. * The company directory returns IDs and runtime slugs, with pagination for large organizations. * Keys support access scopes, expiry, rotation, revocation, and usage metadata. * Existing company keys remain available. Organization keys are staging-only in this release. See [One key for multiple companies](/get-started/organization-api-keys). This changelog tracks the public developer API contract described by `docs/fictix-api`. Internal backend changes should appear here only when they change external behavior, documented limits, or integration expectations. ## 2026-07-17 **Changed:** * `POST /api/v1/demo-jobs` now documents its typed ready-create envelope: `regeneration_required`, lifecycle timestamps, optional simulation/scenario summaries, and a settled credit receipt. * Create errors now distinguish invalid requests, management access, credit admission, idempotency/issuance conflicts, validation, and capacity limits. * The generated Company and Partner OpenAPI references are refreshed from the live production contract during documentation validation. **Client action:** * Accept the current create envelope and preserve `regeneration_required` with the job record. Regenerate a flagged company before a current-contract import. * Handle both the usual `detail` error envelope and the create-time credit envelope with `error` and `message`. **Contract:** * version: `2026-07-17` * tested against: the live production OpenAPI contract ## 2026-07-13 **Added:** * [AI agent integration](/resources/agent-integration) now maps the canonical agent brief to every major tool's instructions file (GitHub Copilot, Windsurf, Cline, Gemini CLI, JetBrains Junie, Replit Agent) instead of only Claude Code and Cursor. * A browser-based app builder section (Lovable, v0, Bolt, Replit) with a paste-ready knowledge prompt teaching the server-side proxy pattern — project API keys never ship in generated frontend code. **Contract:** * version: `2026-07-05` (unchanged — docs-only additions) ## 2026-07-12 **Fixed:** * The [MCP server](/for-agents/mcp) page documented env vars `fictix_mcp` never reads. The server reads `FICTIX_BASE_URL` and `FICTIX_DEFAULT_SLUG` (not `FICTIX_API_ORIGIN`/`FICTIX_PROJECT_SLUG`); the Claude Code, Codex, and Cursor config snippets are corrected. If you copied the old snippets, your sidecar was silently falling back to the production origin with no default slug — update your config. * Replaced the undefined `$FICTIX_ACCESS_TOKEN`/`$FICTIX_BASE_URL` names in the entities-and-exports and skill-pack examples with the real ecosystem names (`FICTIX_ADMIN_API_KEY`, `FICTIX_API_ORIGIN`). * Removed a stale claim on [AI agent integration](/resources/agent-integration) that Fictix ships no MCP server. **Changed:** * [Authentication](/get-started/authentication), [`AGENTS.md`](/AGENTS.md), and the skill pack now name the complete set of credential env vars — the three self-service values plus the Fictix-supplied `FICTIX_ADMIN_API_KEY` (approved partners only) — and explicitly instruct agents not to invent variable names or solicit Console session tokens. * [Start in the Console](/get-started/console-onboarding) now maps the Connect panel's variable names (`FICTIX_API_BASE_URL`, `FICTIX_API_KEY`) to the convention used across these docs. **Contract:** * version: `2026-07-05` (unchanged — docs-only corrections) * client action: if you configured `fictix_mcp` from the old MCP page snippets, rename `FICTIX_API_ORIGIN` → `FICTIX_BASE_URL` and `FICTIX_PROJECT_SLUG` → `FICTIX_DEFAULT_SLUG` in your MCP client config. ## 2026-07-11 **Changed:** * Documented the Console-first onboarding flow for project creation, project API key issuance, rotation, revocation, organization access, and billing. * Clarified that `X-Admin-Key` is an approved server-to-server control-plane credential, not a self-service frontend key. * Added explicit production and staging API origins. * Added response-envelope, capability-matrix, and version/compatibility references. * Added OpenAPI security metadata and typed top-level partner response envelopes, plus CI validation for docs/OpenAPI drift. ![](phosphor/regular/warning) `X-Admin-Key` is an approved server-to-server control-plane credential. It is not a self-service frontend key. **Fixed:** * Updated example `contract_version` values to the current date-based string. * Corrected the stale 5,000-record bundle-cap example to the documented default of 25,000 records. **Contract:** * version: `2026-07-05` * client action: treat `contract_version` as a string and begin onboarding in the Fictix Console. ## 2026-07-06 **Added:** * The ambulatory surgery center (`asc_surgical`) clinical content pack: operating rooms, synthetic surgical cases, clinical incidents, and dictated incident scripts with extraction answer keys. * Five ASC scenario templates for extraction and compliance-style evaluation: ambiguous time references, duplicate staff names, unstated harm levels, mid-sentence corrections, and late reporting gaps. **Client action:** * Select **Ambulatory Surgery Center** in the Console, or set `company.industry` to `asc_surgical` for an approved server-to-server create. * Treat the pack as synthetic evaluation data only. It contains no real patient data and is not for clinical decision-making. **Contract:** * version: `2026-07-05` * tested commit: `5f367d2` ## 2026-07-01 Initial developer-docs source directory. **Added:** * start-here overview * copy-paste quickstart * authentication model * demo job concept docs * company blueprint docs * entity, lineage, and count docs * simulation concept docs * catalog API reference * demo jobs API reference * entity and export API reference * simulation, scenario, and event API reference * test matrix and test run API reference * runtime and native routes API reference * webhook API reference * error, limit, and idempotency reference * build-a-demo-tenant guide * load-into-your-app guide * evaluate-your-app guide * rate limit and quota resource * production checklist * OpenAPI and docs-site publishing guide **Contract notes:** * `/api/v1/demo-jobs` is the preferred external API surface. * `payments` is documented as an alias of `customer_payments`. * `bill_payments` is documented as an alias of `vendor_payments`. * Canonical entity docs expect alias ledgers not to be double-counted. * Runtime/org/native routes are advanced routes, not the first integration path. * Webhooks are documented as feature-entitled, signed, SSRF-guarded, and async. **Operational notes:** * Public docs should be generated from this Markdown plus `/openapi.json`. * Docs CI should fail on route drift, broken links, and contract-version changes without a changelog update. ## Changelog policy Use these labels: | Label | Meaning | | ---------- | ----------------------------------------------------------------- | | Added | New endpoint, field, entity, guide, or capability. | | Changed | Behavior changed without removing compatibility. | | Deprecated | Still works, but clients should migrate. | | Removed | No longer available. Requires migration notes. | | Fixed | Bug fix that affects documented behavior. | | Security | Auth, data isolation, signature, SSRF, or secret-handling change. | Every entry should include: * affected endpoint or concept * old behavior * new behavior * client action * contract version * tested commit or release id ## Breaking change template ```md ## YYYY-MM-DD Changed: - `GET /api/v1/demo-jobs/{job_ref}/entities/{entity}` now returns ... Client action: - Update parsers to read ... Compatibility: - Old field remains until ... Contract: - version: 2026-... - tested commit: ... ``` md # Healthcare data for ambulatory surgery centers Fictix includes an opt-in clinical content pack for **ambulatory surgery centers (ASCs)**. It creates a coherent synthetic facility, procedure schedule, safety-event history, and dictated incident reports for testing healthcare workflows, extraction pipelines, and evaluation harnesses. ![](phosphor/regular/warning) The ASC pack is synthetic test data, not a clinical system. It contains no real patients, patient names, dates of birth, medical-record numbers, or data from healthcare systems. Do not use it for patient care, clinical decision-making, or as a compliance certification. ## Create an ASC company In the Console, select **Ambulatory Surgery Center** when generating a company. For an approved server-to-server demo-job integration, use the canonical industry value in `company.industry`: ```json { "company": { "name": "Lakeside Surgery Center", "domain": "lakeside-surgery.example", "industry": "asc_surgical", "employee_count": 48, "selected_services": ["qbo", "gusto"] }, "generation": { "employees": 48, "months_of_history": 18, "quality_preset": "realistic" } } ``` json `asc_surgical` is an industry profile, not a standard financial archetype. Set `company.industry` rather than relying on `company.archetype` for this vertical. The pack is additive: non-ASC companies retain the ordinary business dataset and expose no clinical records. ## Clinical entity families ASC companies add four canonical entity families. They use the same paged entity and export routes as the rest of the dataset. | Entity | What it contains | Important fields | | -------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | | `operating_rooms` | Operating and procedure rooms in the facility. | `name`, `room_type`, `specialty_tags`, `commissioned_date`, `status` | | `surgical_cases` | Deterministic weekday procedure schedule. | `case_number`, `or_id`, `scheduled_date`, `procedure_code`, `surgeon_name`, `anesthesia_type`, `patient_ref`, `status` | | `clinical_incidents` | Safety events and near misses. | `occurred_at`, `reported_at`, `category`, `harm_level`, `contributing_factors`, `status` | | `dictation_scripts` | Spoken-style incident reports with an extraction answer key. | `incident_id`, `script`, `labels`, `difficulty`, `needle_ids` | `patient_ref` is an opaque synthetic reference such as `PT-00042`; it is never a real patient identifier. Clinical records have deterministic references: rooms link to cases, cases can link to incidents, and dictation scripts link to their incident. Read a runtime page with the project API key: ```bash curl -sS \ "$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/data/clinical_incidents?limit=100&offset=0" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" | jq ``` bash Approved partners can use the normalized equivalent: ```text GET /api/v1/demo-jobs/{job_ref}/entities/clinical_incidents GET /api/v1/demo-jobs/{job_ref}/entities/dictation_scripts/export?format=json ``` text Check `dataset_summary.record_counts` or the public entity catalog before loading. A zero count means the selected company is not an ASC company, or no records of that family were generated. ## Ground truth for extraction and evaluation Each `dictation_scripts` record is rendered from its structured incident, so its `labels` object is the extraction answer key by construction. This makes it useful for testing speech-to-structure, incident triage, and safety/compliance workflows without having to infer a hidden answer from prose. ASC scenario templates can add harder known conditions to the scenario manifest: * `ambiguous-time-reference` * `duplicate-staff-name` * `unstated-harm-level` * `mid-sentence-correction` * `late-report-gap` For those scenarios, `evidence.expected_extraction` is the graded answer-key convention. Preserve the scenario manifest with your test result, and use the answer key only in your evaluation pipeline—not in a user-facing workflow. ## Load order and exports Load the usual financial entities first. For clinical records, use this order: 1. `operating_rooms` 2. `surgical_cases` 3. `clinical_incidents` 4. `dictation_scripts` Use JSON when your integration needs the nested `labels`, factors, or staff references. CSV is useful for flat export workflows, but it does not replace the full structured evaluation payload. Next: [Entities And Exports](/api-reference/entities-and-exports), [Entities And Lineage](/concepts/entities-and-lineage), and [Simulation, Scenarios & Events](/api-reference/simulation-scenarios-events). # Partner integration checklist Work through this checklist before your integration relies on Fictix in a real workflow. It covers what "production-ready" means for the API contract, auth and secrets, data quality, exports, simulation and runtime, and webhooks — the areas an integration team needs to confirm before launch. ## API contract * `/api/v1/demo-jobs` is the primary external surface. * `/api/v1/catalog/*` endpoints return current service, entity, capability, and export-pack metadata. * `/api/v1/demo-jobs/{job_ref}/status` returns generated status, company summary, dataset summary, quality summary, and simulation summary. * `/api/v1/demo-jobs/{job_ref}/quality` returns validation checks and a certificate. * `/api/v1/demo-jobs/{job_ref}/runtime-readiness` reports static, trickle, and continuous-readiness state. * `/api/v1/demo-jobs/{job_ref}/entities/{entity}` supports canonical entity aliases. * `/api/v1/demo-jobs/{job_ref}/entities/{entity}/export` supports JSON and CSV. * `/api/v1/demo-jobs/{job_ref}/bundle` fails with `413` rather than returning oversized responses. * Runtime/native routes remain documented as advanced routes, not as the first integration path. ## Auth and secrets * Customer onboarding begins in the Fictix Console, where project keys are created, rotated, and revoked. * Management auth is enabled for creating and controlling demo jobs. * Runtime API keys are generated per project. * Runtime API key prefixes are safe for display; full keys are not. * API keys are never written to frontend logs. * Webhook secrets are accepted on create and never returned. * Staging and production use separate credentials. * Local development credentials are not used in staging docs or examples. * Secret values are redacted from request, response, and error telemetry. ## Data quality * Generated counterparty domains use reserved synthetic namespaces such as `.example`, `.test`, `.invalid`, or a Fictix-owned fake namespace. * Entity counts in project metadata match fetchable entities. * Canonical ledgers do not duplicate alias ledgers in export packs. * `payments` is treated as an alias of `customer_payments`. * `bill_payments` is treated as an alias of `vendor_payments`. * `bank_transactions.bank_account_id` resolves to an exported bank account. * invoice line `item_id` resolves to `items`. * bill line `item_id` resolves to `items`. * journal entries balance within a one-cent tolerance. * invoice totals reconcile against amount paid and balance. * bill totals reconcile against amount paid and balance. * payroll gross, tax, benefit, and net pay reconcile. * validation certificates are generated and exposed. ## Export readiness * Runtime CSV export pack returns a real ZIP file. * ZIP contains all canonical ledgers that the UI advertises. * ZIP includes a README or schema document. * ZIP row counts reconcile against `record_counts`. * Duplicate alias files are omitted or explicitly documented. * Normalized line files are included when available: * `invoice_lines.csv` * `bill_lines.csv` * `journal_entry_lines.csv` * `payment_applications.csv` * CSV exports preserve stable headers. * JSON-in-CSV cells are documented when unavoidable. * Empty ledgers are either included intentionally or excluded consistently. ## Simulation and runtime * Simulation can start from a generated job. * Simulation status returns current simulated time, mode, and state. * Advancing time respects maximum advance limits. * Step, pause, resume, dials, checkpoint, and restore routes are covered by tests. * Events are written when simulation or scenario actions mutate state. * Runtime reads remain tenant-scoped by project. * Native-shaped routes for supported services return deterministic data for the same seed and company state. * Runtime writes either mutate state consistently or return a documented unsupported-operation response. ## Webhooks * Webhook creation requires the `webhooks` entitlement. * Callback URLs are SSRF-guarded. * Subscription secrets are not returned. * Delivery payloads are HMAC-SHA256 signed when a secret is configured. * Delivery worker is running in staging/production. * Durable outbox tables exist and migrations are applied. * Duplicate delivery is possible and documented. * Test event delivery works against a known receiver. * Delivery failures are visible in telemetry. ## Observability * Errors include request ids or trace ids when available. * Webhook delivery failures include project id, event type, and sanitized error. ## Partner launch gate Do not launch a partner integration until all of the following are true: * create job works in staging with management auth * Console-created project can create/rotate a runtime key and make a server-side runtime request in staging * status reaches `ready` * quality certificate is present * entity pagination works for every advertised canonical entity * CSV export works for every advertised canonical entity * full CSV export pack downloads as ZIP * runtime API key can read native-shaped data for selected services * webhooks can deliver a signed test event * simulation can advance and produce inspectable events * test matrix and test run flows complete * the docs site points to the same contract version as the deployed API # Errors, limits, and idempotency This page documents error handling expectations for Fictix integrations. ## Error shape FastAPI routes may return simple string details: ```json { "detail": "No data. Generate first." } ``` json or structured details: ```json { "detail": { "code": "project_dataset_limit_exceeded", "message": "Project dataset exceeds current blob storage safety budget" } } ``` json Clients should handle both. `POST /api/v1/demo-jobs` can also return a credit envelope when admission or credit settlement fails: ```json { "error": "insufficient_credits", "message": "A valid credit run or sufficient credits are required." } ``` json ## Common status codes | Status | Meaning | Client action | | ------ | --------------------------------------------------------------------------- | ------------------------------------------------------------------------ | | `400` | Bad request, invalid delta, missing confirmation, no generated data | Fix request or create/generate data first. | | `401` | Missing or expired auth | Refresh credentials and retry once. | | `402` | No valid credit run or insufficient credits for a demo-job create | Fund or resolve the server-side credit run; do not retry blindly. | | `403` | Authenticated but not allowed | Check org membership, role, or credential scope. | | `404` | Job, project, entity, matrix, run, event, or checkpoint not found | Verify id and tenant scope. | | `409` | Idempotency conflict or active generation conflict | Use a new idempotency key or wait for active run. | | `413` | Bundle or dataset too large | Page entities or export per entity. | | `422` | Invalid request body, priced generation shape, or credit validation failure | Fix request fields or resolve credit state before retrying. | | `429` | Generation, rate, plan, or concurrency limit | Back off and retry after the applicable limit clears. | | `500` | Internal failure | Retry safely when idempotent; otherwise contact support with request id. | | `503` | Service not ready | Retry with backoff; check readiness. | ## Idempotency POST`/api/v1/demo-jobs` `POST /api/v1/demo-jobs` requires: ```http Idempotency-Key: ``` http If a network failure occurs after the server creates a job, retry the exact same request with the exact same idempotency key. Fictix returns the cached response. If you send a different body with the same key, Fictix returns: ```http 409 Conflict ``` http ### Native writes Native writes require a nonempty idempotency key. Send `Idempotency-Key` unless the [provider workflow](/api-reference/provider-workflows) documents another header or body field for that route. Give each new operation a new key; reuse the original key and payload when retrying it. Read operations, including POST requests modeled as reads, do not require a native-write idempotency key. For Mercury recipient creation (`POST /api/v1/recipients`), send the key in the `Idempotency-Key` header. The recipient JSON does not accept `idempotencyKey`; that body field is supported for Mercury payment and transfer requests. ## Bundle limit GET`/api/v1/demo-jobs/{job_ref}/bundle` `GET /api/v1/demo-jobs/{job_ref}/bundle` is intentionally capped. When requested entity records exceed the cap: ```json { "detail": "Bundle too large for one response. Requested 30001 records across 15 entities, which exceeds the partner bundle cap of 25000. Use the entity or export endpoints instead." } ``` json ![](phosphor/regular/warning) The active cap is deployment-configurable. Do not parse the number from an error message; handle `413` by paging or exporting instead. Switch to: ```text GET /api/v1/demo-jobs/{job_ref}/entities/{entity} GET /api/v1/demo-jobs/{job_ref}/entities/{entity}/export ``` text ## Generation budget limit Large generation shapes can be rejected before expensive work starts. The error explains: * weighted load * estimated records * quality preset * connector count * budget * safe employees/months suggestions To fix: * lower `generation.employees` * lower `generation.months_of_history` * reduce `selected_services` * choose a less aggressive quality preset * create multiple smaller jobs ## Retry guidance Safe to retry with same idempotency key: * network timeout during `POST /demo-jobs` * `503` * `500` before receiving a response body Safe to retry without idempotency key: * `GET` requests * `DELETE` after verifying semantics are acceptable ![](phosphor/regular/warning) Do not blindly retry: * reset * clone * publish if your client cannot tolerate duplicate side effects * test run submission unless your client can dedupe by matrix/run id ## Request IDs When available, capture response headers such as request id or trace id from errors and include them in support tickets. Recommended client logging: * request method * path, without secrets * status code * job id * entity * idempotency key hash, not raw key * response request id header * response body detail # 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](https://console.fictix.ai) 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: ```text /api/v1/demo-jobs ``` text For the stable response fields, contract version, and error envelope, see [Response Envelopes](/api-reference/response-envelopes) and the live [`/openapi.json`](https://api.fictix.ai/openapi.json). ## Create a demo job POST`/api/v1/demo-jobs` ```http POST /api/v1/demo-jobs ``` http Headers: ```http Content-Type: application/json Idempotency-Key: X-Admin-Key: $FICTIX_ADMIN_API_KEY ``` http ![](phosphor/regular/warning) `/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: ```json { "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 } ``` json ### Required fields Minimum viable body: ```json { "company": { "name": "Acme Logistics" }, "generation": { "employees": 20, "months_of_history": 6, "quality_preset": "clean" } } ``` json ### Response ```json { "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 } ``` json 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` ```http GET /api/v1/demo-jobs?limit=100&offset=0 ``` http Query params: | Name | Type | Default | Notes | | -------- | ------- | ------- | ------------------------------------------------ | | `limit` | integer | `100` | Minimum `1`, maximum follows API pagination cap. | | `offset` | integer | `0` | Use for offset pagination. | Response: ```json { "object": "list", "contract_version": "2026-07-17", "count": 1, "jobs": [] } ``` json ## Get demo job GET`/api/v1/demo-jobs/{job_ref}` ```http GET /api/v1/demo-jobs/{job_ref} ``` http `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` ```http GET /api/v1/demo-jobs/{job_ref}/status ``` http 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` ```http GET /api/v1/demo-jobs/{job_ref}/quality ``` http 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` ```http GET /api/v1/demo-jobs/{job_ref}/certificate ``` http 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` ```http GET /api/v1/demo-jobs/{job_ref}/runtime-readiness ``` http Returns explicit readiness across static, trickle, and continuous runtime modes. ## Get eval pack manifest GET`/api/v1/demo-jobs/{job_ref}/eval-pack-manifest` ```http GET /api/v1/demo-jobs/{job_ref}/eval-pack-manifest ``` http Returns a shareable eval/data-pack contract including certificate, readiness, and scenario information. ## Publish POST`/api/v1/demo-jobs/{job_ref}/publish` ```http POST /api/v1/demo-jobs/{job_ref}/publish ``` http Returns the updated job plus handoff metadata: ```json { "handoff": { "publish_id": "pub_abc123", "dashboard_url": "https://...", "staging_urls": {} } } ``` json ## Clone POST`/api/v1/demo-jobs/{job_ref}/clone` ```http POST /api/v1/demo-jobs/{job_ref}/clone ``` http 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` ```http POST /api/v1/demo-jobs/{job_ref}/reset ``` http Body: ```json { "confirm": true } ``` json Returns: ```json { "object": "demo_job_reset", "contract_version": "2026-07-17", "status": "reset", "job": {} } ``` json ![](phosphor/regular/warning) Use reset carefully. Clone is safer for normal iteration because reset destroys current generated state. # Create and pay QuickBooks test documents > **Staging verified:** This workflow passed on November 3, 2026 with a Console-created synthetic company and ordinary runtime credentials. Production qualification is pending. Supported operations are described below. Use a synthetic company with QuickBooks selected and a Developer or higher plan with native API access. Generation credits and native API access are separate; adding generation credits does not enable native routes. See [Connect your software](/guides/connect-your-software) for current access requirements. Create the company in the Console, then create a **Read and modify** company key in **Settings → Company → Company API keys**. Keep it on your server. You can also use a staging [organization key](/get-started/organization-api-keys) with the same access for the organization that owns the company. Use the matching origin and slug from that company's Connect panel: ```bash export FICTIX_API_ORIGIN="https://api-staging.fictix.ai" export FICTIX_PROJECT_SLUG="" export FICTIX_PROJECT_API_KEY="" QBO_BASE="$FICTIX_API_ORIGIN/api/v1/orgs/$FICTIX_PROJECT_SLUG/native/qbo/v3/company/test" ``` bash These requests change only the synthetic company's records. They do not send money through a real QuickBooks or bank connection. ## Read the IDs your application will use Query customers, vendors, items and ledger accounts. Replace `Account` in this request with `Customer`, `Vendor` or `Item` for the other lists: ```bash curl --fail-with-body -sS --get "$QBO_BASE/query" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ --data-urlencode 'query=SELECT * FROM Account STARTPOSITION 1 MAXRESULTS 1000' ``` bash Read the records inside `QueryResponse.Account`, `QueryResponse.Customer`, `QueryResponse.Vendor` and `QueryResponse.Item`. Use their returned `Id` values in native references. In particular, `AccountRef.value` refers to a native `Account.Id`; do not substitute a different ID from an export or canonical data response. Choose an active service item with an income-account mapping, an expense account, and the bank-linked cash account for this company. Invoice creation resolves the item's income account. Payment posting requires exactly one active bank account linked to the selected cash ledger account, using the document's currency. An ambiguous or missing mapping returns a validation error before posting. The [query reference](/api-reference/provider-workflows#quickbooks-query-and-amount-behavior) describes supported filters and paging. Queries and single-record GETs also support `Invoice`, `Bill`, `Payment`, `BillPayment` and `JournalEntry`. ## Create an invoice Save this as `invoice.json`, replacing the customer and item IDs with values you just read. Use your transaction date in `YYYY-MM-DD` format. ```json { "CustomerRef": {"value": ""}, "TxnDate": "2026-11-03", "Line": [{ "Amount": 0.01, "DetailType": "SalesItemLineDetail", "SalesItemLineDetail": { "ItemRef": {"value": ""}, "Qty": 1, "UnitPrice": 0.01 } }] } ``` json ```bash curl --fail-with-body -sS "$QBO_BASE/invoice" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: qbo-invoice-demo-1" \ --data-binary @invoice.json ``` bash A successful response contains `Invoice.Id`, `TotalAmt: 0.01` and `Balance: 0.01`. Creation posts the receivable and revenue. It does not record a cash receipt. Save `Invoice.Id` for the payment. Use a distinct idempotency key for each operation. Persist the key and exact payload before sending. If the response is lost, retry with that same key and body; changing the payload under the same key returns `409`. Use a new key for a genuinely new invoice. ## Apply the customer's payment Save this as `payment.json`. Use the same customer, the cash account's native ID, and the returned invoice ID: ```json { "CustomerRef": {"value": ""}, "TxnDate": "2026-11-03", "TotalAmt": 0.01, "DepositToAccountRef": {"value": ""}, "Line": [{ "Amount": 0.01, "LinkedTxn": [{"TxnId": "", "TxnType": "Invoice"}] }] } ``` json ```bash curl --fail-with-body -sS "$QBO_BASE/payment" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: qbo-payment-demo-1" \ --data-binary @payment.json ``` bash The response contains `Payment.Id`. Read the invoice again at `GET /invoice/{Invoice.Id}`: its balance should now be zero. The payment creates the linked bank transaction and balanced cash/receivable posting. ## Create a vendor bill Save this as `bill.json` using the vendor and expense account IDs: ```json { "VendorRef": {"value": ""}, "TxnDate": "2026-11-03", "Line": [{ "Amount": 0.01, "DetailType": "AccountBasedExpenseLineDetail", "AccountBasedExpenseLineDetail": { "AccountRef": {"value": ""} } }] } ``` json ```bash curl --fail-with-body -sS "$QBO_BASE/bill" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: qbo-bill-demo-1" \ --data-binary @bill.json ``` bash A successful response contains `Bill.Id` and an outstanding balance of `0.01`. The posting increases expense and accounts payable; cash is unchanged. ## Pay the bill Save this as `billpayment.json`. This workflow supports `PayType: "Check"`: ```json { "VendorRef": {"value": ""}, "TxnDate": "2026-11-03", "TotalAmt": 0.01, "PayType": "Check", "CheckPayment": {"BankAccountRef": {"value": ""}}, "Line": [{ "Amount": 0.01, "LinkedTxn": [{"TxnId": "", "TxnType": "Bill"}] }] } ``` json ```bash curl --fail-with-body -sS "$QBO_BASE/billpayment" \ -H "X-API-Key: $FICTIX_PROJECT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: qbo-billpayment-demo-1" \ --data-binary @billpayment.json ``` bash Read `GET /bill/{Bill.Id}` to confirm a zero balance. The payment creates the linked bank withdrawal and balanced payable/cash posting. Retrying an operation with its original key and payload must not create another payment. ## Check the resulting books Query `JournalEntry`. Each line contains a numeric `Amount`, `DetailType: "JournalEntryLineDetail"`, and a `JournalEntryLineDetail` object with `PostingType: "Debit"` or `"Credit"` and a resolvable `AccountRef`. Each journal's debit and credit amounts must balance. Existing document IDs must remain stable as your application writes new records. The four operations above create four balanced journals. After both exact payments, the new invoice and bill have zero outstanding balance, and their opposite cash movements cancel. You can also read canonical entities or download a [CSV export](/guides/load-into-your-app) to reconcile the company's receivables, payables and bank balances with its ledger. ## Supported slice This guide covers new single-currency, non-tax service invoices, expense bills, and exact payments linked to one existing document. The party, currency, amount, active accounts and mappings must agree. Unsupported invoice or bill corrections return `409`; creating a payment does not authorize editing a posted document. Inventory, tax and currency-conversion workflows are outside this example. Direct `JournalEntry` writes are rejected; journal reads expose the actual posted accounting consequences. Validation errors return a QBO `Fault` or a Fictix error body. Check the HTTP status and body before treating an operation as accepted. The broader [provider workflow reference](/api-reference/provider-workflows) and [Intuit's accounting documentation](https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/invoice) describe the wire conventions; supported Fictix operations remain bounded by this guide. # Runtime and native routes Staging also accepts [organization API keys](/get-started/organization-api-keys) on runtime routes that accept a company key. Each URL still selects one company by slug. Routes documented as requiring a Console bearer session retain that requirement. Runtime routes are the project-scoped APIs exposed for a generated company. Console customers use them to connect an existing company to their own app; approved server-to-server integrations use `/demo-jobs` when they need to create or control normalized jobs programmatically. For Console customers, runtime routes are the normal first integration surface: create/select a company in the [Fictix Console](https://console.fictix.ai), then copy its project key and base URL from **Connect to your builder** or **Company API keys**. See [Start In The Fictix Console](/get-started/console-onboarding). Runtime routes use: ```http X-API-Key: ``` http Create a runtime key on the company's API page or under **Company API keys**. The full secret is shown at creation or rotation. The staging API page also provides a provider selector and a tested first request. Start with [Connect your software](/guides/connect-your-software). ## Runtime info GET`/api/v1/orgs/{slug}/info` ```http GET /api/v1/orgs/{slug}/info ``` http Returns generated company metadata and runtime details. ## Capabilities GET`/api/v1/orgs/{slug}/capabilities` ```http GET /api/v1/orgs/{slug}/capabilities ``` http Returns capability metadata available for the generated company. ## Execute capability POST`/api/v1/orgs/{slug}/execute` ```http POST /api/v1/orgs/{slug}/execute ``` http Executes a named capability against the generated company runtime. Read capabilities need `read` and `execute` scopes; write capabilities need `write` and `execute`. The body's legacy `org_id` is the company project ID, not the workspace organization ID or company slug. Check `status` in the JSON response: an execution error may use HTTP 200. FCI provides derived financial summaries through this surface and retains preview status. It has no native route and is not selectable in the standard connector picker. Confirm capability availability for your company. See the [FCI example](/guides/connect-your-software#5-use-fci-through-execute). ## Browse canonical data GET`/api/v1/orgs/{slug}/data/{entity}` ```http GET /api/v1/orgs/{slug}/data/{entity} ``` http Use this when you intentionally work with the org-scoped runtime instead of the partner demo-job surface. ## Export canonical data GET`/api/v1/orgs/{slug}/export/{entity}` ```http GET /api/v1/orgs/{slug}/export/{entity} ``` http ## CSV ledger bundle GET`/api/v1/orgs/{slug}/export-pack/csv` ```http GET /api/v1/orgs/{slug}/export-pack/csv ``` http Returns a zip archive containing canonical CSV ledgers and docs. Expected bundle behavior: * one zip file * all canonical ledgers * README * schema metadata * normalized line files where available * no duplicate `payments.csv` and `customer_payments.csv` * no duplicate `bill_payments.csv` and `vendor_payments.csv` ## Provider export packs GET`/api/v1/orgs/{slug}/export-pack/{target}` ```http GET /api/v1/orgs/{slug}/export-pack/qbo GET /api/v1/orgs/{slug}/export-pack/hubspot GET /api/v1/orgs/{slug}/export-pack/asana GET /api/v1/orgs/{slug}/export-pack/{target} ``` http Schema routes: ```http GET /api/v1/orgs/{slug}/export-pack/qbo/schema GET /api/v1/orgs/{slug}/export-pack/hubspot/schema GET /api/v1/orgs/{slug}/export-pack/asana/schema GET /api/v1/orgs/{slug}/export-pack/{target}/schema ``` http ## Native service routes Native routes emulate provider-shaped APIs for selected systems. Public catalog snapshots observed on October 28, 2026 list 60 entries in both staging and production. They mark 59 entries available/selectable with native support and one FCI execute preview as not selectable; all entries report approximate fidelity. These catalog values do not verify individual requests or promise strict upstream parity. The published first-request templates were tested in staging only. Use the [provider directory](/api-reference/provider-first-requests) for exact paths, POST bodies, content types, and catalog status, and the [workflow reference](/api-reference/provider-workflows) for staging-verified operations. Before calling a provider/object route, query the public [Capability Matrix](/api-reference/capability-matrix) and catalog object-schema endpoint. The company and selected services determine whether a path has `native`, `generic`, or no support. Native routes also require the `native.genome` entitlement. The Console shows the active plan and feature availability for the organization that owns the company. The routing pattern is: ```http /api/v1/orgs/{slug}/native/{service}/{provider_path} ``` http `provider_path` can include API versions, account IDs, a GraphQL endpoint, an XML gateway, or a form endpoint. It is not a universal object CRUD path. For example: ```http GET /api/v1/orgs/{slug}/native/stripe/v1/customers GET /api/v1/orgs/{slug}/native/qbo/v3/company/test/query?query=SELECT%20*%20FROM%20Customer POST /api/v1/orgs/{slug}/native/monday/v2 POST /api/v1/orgs/{slug}/native/sage_intacct/ia/xml/xmlgw.phtml ``` http Use the canonical provider ID `qbo` for QuickBooks requests. Native reads need `read`; native writes need `write`. Some reads use POST. Writes require a nonempty idempotency key. Send a fresh `Idempotency-Key` header for each operation, unless the provider workflow documents another header or body field for that route. Reuse the key only when retrying the same operation and payload. Inspect provider-level errors as well as HTTP status. ## Choosing demo jobs vs runtime routes Use `/demo-jobs` when: * you are creating companies * you want normalized data * you need the stable partner contract * you need quality/certificate/readiness metadata * you do not want to manage runtime API keys Use runtime routes when: * your product specifically needs `X-API-Key` isolation * you are testing provider-shaped API behavior * you need raw export packs * you are building internal tooling against generated companies ## Key lifecycle Project API keys are created, rotated, and revoked in the Console. They are scoped to a single generated company. If a key is exposed, rotate it in the Console and update your server-side secret before revoking the old key. ![](phosphor/regular/warning) Never use an `X-Admin-Key` or a project key in frontend JavaScript. The Console is the frontend entry point; runtime keys belong only in server-side workloads. # Authentication Fictix exposes a Console-first access model. Use the narrowest layer that supports your workflow, and obtain customer-facing credentials from the [Fictix frontend](https://console.fictix.ai/auth/sign-in?next=%2Fprojects)—not from browser source code or a shared admin secret. ## Choose a runtime key | Key | Company access | Where to create it | | ---------------- | ---------------------------------------------------- | ----------------------------------------------------- | | Company key | One generated company | Company settings → Company API keys | | Organization key | Every current and future company in one organization | Staging organization settings → Organization API keys | Both use `X-API-Key`. Organization keys are currently available in staging; see [One key for multiple companies](/get-started/organization-api-keys) for company selection, scope settings, and complete examples. ## Project runtime API key For a company you created or selected in the Console, open [Company settings](https://console.fictix.ai/settings/company) and create a project-scoped key under **Company API keys**. This is the normal key for connecting your server to generated company data and native-shaped routes. ```http X-API-Key: ``` http ![](phosphor/regular/info) The project's **Connect to your builder** panel supplies the exact base URL, slug, environment variable names, and first request. Project administrators can rotate or revoke keys in the Console. The full key is only displayed when it is created or rotated. Use project keys only under the generated company runtime: ```text GET /api/v1/orgs/{slug}/info GET /api/v1/orgs/{slug}/data/{entity} GET /api/v1/orgs/{slug}/export/{entity} GET /api/v1/orgs/{slug}/native/{service}/{object_type} POST /api/v1/orgs/{slug}/native/{service}/{object_type} ``` text Do not send a project key to `/api/v1/demo-jobs` control-plane routes. For provider calls, native reads need `read`, and native writes need `write`. Some native reads use POST with JSON, GraphQL, XML, or form bodies. Execute reads need both `read` and `execute`; execute writes need `write` and `execute`. FCI uses the execute transport. The staging company's **API** page provides provider-specific first requests and indicates the required access. See [Connect your software](/guides/connect-your-software) for full examples. ## Console bearer session The signed-in Fictix Console uses a WorkOS-backed bearer session for its own management flows, including creating or controlling companies through `/api/v1/demo-jobs`. The Console manages this session in the app; it is not a customer integration credential, has no customer environment variable, and must never be copied from browser tools or forwarded to another service. ## Approved external management credential External server-to-server callers using management creation and lifecycle routes under `/api/v1/demo-jobs` must use the Fictix-supplied admin key: `FICTIX_ADMIN_API_KEY` as `X-Admin-Key`. Admin-key example: ```http X-Admin-Key: $FICTIX_ADMIN_API_KEY ``` http ![](phosphor/regular/warning) This credential is not a self-service customer credential. Do not use an email/password login, copy a bearer token from browser developer tools, or put the admin key in frontend code. Normal Console customers should create or select a company in the Console, which manages its own WorkOS bearer session, and use the project `X-API-Key` for subsequent runtime access instead. ## Idempotency `POST /api/v1/demo-jobs` requires an idempotency key when you have approved control-plane access: POST`/api/v1/demo-jobs` ```http Idempotency-Key: ``` http Rules: * Use a unique key for each new company creation. * Reuse the same key only when retrying the same request body. * If the same key is reused with a different body, Fictix returns `409`. * Treat `409` as a client bug or stale retry state. Good keys: ```text customer-demo-acme-2026-07-02 load-test-run-7dbe3a8c crm-import-smoke-20260702T120000Z ``` text Bad keys: ```text demo test retry ``` text ## Tenant scope Approved management integrations are scoped by the Fictix account and tenant permissions attached to their server-side credential. Project runtime keys are scoped to one generated company. For customer-facing products, keep each customer/environment in the appropriate organization and rotate a project key instead of sharing it between unrelated integrations. An organization runtime key spans all companies currently owned by its organization and can list them through `GET /api/v1/organizations/{org_id}/companies`. It does not grant management authority. Use a separate key per integration so each can be revoked independently. ## Browser safety ![](phosphor/regular/warning) Never expose: * `X-Admin-Key` * raw company or organization runtime API keys * Stripe/WorkOS/server secrets If browser code needs to initiate a customer action, keep the credentialed operation in the Fictix Console or proxy it through your backend. A safe flow looks like: ```text Browser -> Fictix Console UI or your backend -> Fictix management API ``` text not: ```text Browser -> Fictix with project or admin key in JavaScript ``` text ## Common auth failures ### Missing management credentials Symptom: ```json { "detail": "Not authenticated" } ``` json Fix: * for an external integration, verify it is approved and send `X-Admin-Key: $FICTIX_ADMIN_API_KEY` * in the Console, stay signed in so the Console can manage its bearer session * verify the environment points to the intended Fictix host ### Wrong auth layer Symptom: ```json { "detail": "Project API key is not valid for this route" } ``` json Fix: * for an external integration, use the Fictix-supplied `X-Admin-Key: $FICTIX_ADMIN_API_KEY` for `/demo-jobs`; the Console uses its managed WorkOS bearer session * use company runtime keys under `/orgs/{slug}`; staging organization keys also access their own `/organizations/{org_id}/companies` directory ### Expired bearer token Symptom: ```json { "detail": "Token expired" } ``` json Fix: * rotate or renew the approved admin key through Fictix, or sign in again to refresh the Console session * retry once with the renewed credential * never copy a bearer token from browser developer tools or a Console session # MCP server The Fictix backend includes `fictix_mcp`, an MCP server that adapts the Fictix API into tools, resources, and prompts an MCP-aware agent can call directly, instead of the agent hand-rolling `curl` calls from [AGENTS.md](/for-agents/consuming-these-docs). It's a companion to the project-file snippets on [AI agent integration](/resources/agent-integration) — use those if you just want an `AGENTS.md`/skill file, use this if your client speaks MCP. ![](phosphor/regular/info) [Download the standalone MCP adapter](/downloads/fictix-mcp-0.1.1.zip). It installs independently of the backend repository and includes the public API guides. Use a company you create in Console and its normal API key. Native and execute tools retain the API's plan and scope restrictions. ![](phosphor/regular/warning) Never put `FICTIX_PROJECT_API_KEY` or any other Fictix credential in browser JavaScript or commit it to a repo. Configure the server with environment variables and keep it running server-side or on your own machine, the same as any other project API key. ## What it exposes * **Tools** that wrap the REST surface — reading entities and pages, discovering the catalog, and creating/controlling demo jobs. * **Resources** for pulling Fictix data (an entity page, a catalog listing) into an agent's context without a manual tool call. * **Prompts** that package common Fictix tasks — for example, loading a generated company into a target app — into a reusable starting point. * **Read/write safeguards**: read-only tools operate under the same project-scoped `X-API-Key` as the REST routes they wrap. Tools that create or control a company follow the same rules as `POST /api/v1/demo-jobs` directly — approved control-plane access (`FICTIX_ADMIN_API_KEY`, see below) and a stable `Idempotency-Key` — so running the server doesn't by itself grant write access you don't already have. ## Install and run Use Python 3.12 or later in a dedicated environment: ```bash python3.12 -m venv .venv .venv/bin/python -m pip install https://fictix-docs.vercel.app/downloads/fictix-mcp-0.1.1.zip .venv/bin/python -m fictix_mcp --help ``` bash The [archive manifest](/downloads/fictix-mcp-0.1.1.manifest.json) lists its SHA-256 and reviewed source files. This direct download is the supported installer; there is no PyPI or npm registry release yet. Do not substitute `pip install fictix-mcp`, `uvx fictix-mcp`, or `npx fictix-mcp`. After configuring the three environment variables below, run: ```bash .venv/bin/python -m fictix_mcp ``` bash `stdio` is the default transport. Your MCP client spawns the process and talks through stdin/stdout. Use the absolute path to this environment's Python in client configuration; a bare `python` can select an environment without the adapter. On Windows, the environment's executable is `.venv\Scripts\python.exe`. Start with `project_info`, then `browse_entity` with `entity="bills"`, `limit=25`, and `offset=0`. Page until the returned count is exhausted. The `fictix://docs/quickstart` resource and `search_docs` work from bundled public documentation, without a backend checkout. Streamable HTTP and SSE are optional local transports: `--transport streamable-http` or `--transport sse`, with `--host` and `--port`. Keep the default loopback host unless you have configured access control for other clients. ## Configure `fictix_mcp` reads its own environment variable names — they are **not** the `FICTIX_API_ORIGIN`/`FICTIX_PROJECT_SLUG` shell conventions the curl examples elsewhere in these docs use. The values are the same ones, from the same place — your project's **Connect to your builder** panel in the [Fictix Console](https://console.fictix.ai) — but the server only reads these names: ```bash FICTIX_BASE_URL # API origin, e.g. https://api.fictix.ai or https://api-staging.fictix.ai FICTIX_DEFAULT_SLUG # project slug, from Console > Connect to your builder FICTIX_PROJECT_API_KEY # from Console > Company settings > Company API keys ``` bash One optional variable enables the management tools (creating or controlling companies via demo jobs). It is for Fictix-approved server-to-server integrations only — a normal Console customer does not have it and cannot self-serve it: ```bash FICTIX_ADMIN_API_KEY # approved management key, sent as X-Admin-Key ``` bash The signed-in Console uses a managed WorkOS bearer session for its own management actions. That session is not available as a copyable MCP credential; external server-to-server MCP management uses the Fictix-supplied `FICTIX_ADMIN_API_KEY` only. If it is not set, the read-only tools still work under the project API key — management tool calls go out uncredentialed and come back with the same `401` that `POST /api/v1/demo-jobs` itself returns. ## Client config ### Claude Code ```bash claude mcp add fictix \ -e FICTIX_BASE_URL=https://api.fictix.ai \ -e FICTIX_DEFAULT_SLUG= \ -e FICTIX_PROJECT_API_KEY= \ -- /absolute/path/to/.venv/bin/python -m fictix_mcp ``` bash ### Codex Add a `[mcp_servers.fictix]` block to `~/.codex/config.toml`: ```toml [mcp_servers.fictix] command = "/absolute/path/to/.venv/bin/python" args = ["-m", "fictix_mcp"] env = { FICTIX_BASE_URL = "https://api.fictix.ai", FICTIX_DEFAULT_SLUG = "", FICTIX_PROJECT_API_KEY = "" } ``` toml See the [official Codex MCP configuration reference](https://learn.chatgpt.com/docs/extend/mcp?surface=cli) for command and environment settings. ### Cursor Add a `.cursor/mcp.json` in your project (or user config directory): ```json { "mcpServers": { "fictix": { "command": "/absolute/path/to/.venv/bin/python", "args": ["-m", "fictix_mcp"], "env": { "FICTIX_BASE_URL": "https://api.fictix.ai", "FICTIX_DEFAULT_SLUG": "", "FICTIX_PROJECT_API_KEY": "" } } } } ``` json ## Sidecar today, not a hosted endpoint `fictix_mcp` is a process your agent (or you) run locally or on your own infrastructure — there is no hosted `mcp.fictix.ai`-style endpoint to point a client at instead. If your workflow needs a shared, always-on MCP endpoint rather than a per-developer local process, that's a gap worth raising with your Fictix contact; it isn't available yet.