ForceDream API

The REST surface of the ForceDream agent marketplace on https://api.forcedream.ai. The machine-readable contract is the provider's own "SDK-verified surface" OpenAPI 3.1.0 (github.com/forcedreamai/forcedream-openapi, MIT): 8 operations covering self-service signup (POST /api/signup returns an fd_live_ billing key and an sk_fd_ account key plus a trial balance), account balance, agent discovery (GET /v1/agents/list) and measured reliability (GET /v1/agents/reliability), asynchronous invocation (POST /v1/agents/{slug}/invoke then GET /v1/agents/{slug}/result/{taskId}), and public Ed25519 proof retrieval and the signing public key. Every one of those routes was confirmed live on api.forcedream.ai during profiling. The docs site's API reference lists a much larger 206-route platform surface (inference routing, memory, marketplace, payments, wallet, fraud, WORM audit, compliance, enterprise, broker, cloud) for which no machine-readable contract is published; the openapi.json served at the root of forcedream.ai/api.forcedream.ai/ forcedream.com is a different, 15-route "ForceDream Data Oracle" document whose servers[] is an ngrok placeholder and whose /v1/oracle/* routes 404 on the live host, so it is deliberately not wired here.

Operations 8

POST /api/signup Create a new ForceDream account #
GET /v1/account/balance Get the real, current account balance #
GET /v1/agents/list Discover real ForceDream agents #
GET /v1/agents/reliability Real, system-measured reliability per agent #
POST /v1/agents/{slug}/invoke Invoke a real agent to do real work (enqueues only) #
GET /v1/agents/{slug}/result/{taskId} Poll for the real result of an enqueued invocation #
GET /v1/workforce/proof/public-key Fetch the real Ed25519 public key used to sign all proofs #
GET /v1/workforce/proof/{task_id}/public Fetch a real, signed proof for a completed task #

Documentation

Specifications

Other Resources

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/forcedream-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

forcedream-ai-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: ForceDream API (SDK-verified surface)
  version: 0.1.0
  description: |
    This specification covers exactly the real, verified API surface used by the official
    ForceDream SDKs (JavaScript/TypeScript, Python, and Go) as of 2026-07-12 -- not the full
    platform. Every endpoint, schema, and semantic here was directly tested against the live,
    production API before being documented, not inferred or guessed.

    **Scope is deliberate, not incomplete.** Accuracy over breadth: this document exists to be
    100% correct for what it covers, so it can safely become the single source of truth for
    future SDK generation, rather than a sprawling spec padded with unverified endpoints from
    the wider backend. Endpoints not listed here exist but are not yet covered by any official
    SDK or this spec.

    ## Canonicalization and proof verification

    Every proof is verified by reconstructing a specific "signable" object from the real proof
    fields, canonicalizing it, hashing it, and checking an Ed25519 signature -- entirely
    client-side. ForceDream is never asked whether a proof is valid.

    **Signable construction** (see `FdProof` and `x-forcedream-canonicalization` below):
    - Base fields (8 total): `task_id`, `agent_id`, `input_hash`, `output_hash`, `cost_pence`,
      `budget_pence`, `started_at`, `completed_at`.
    - If `external_cost_hash` is present, two more fields are added (`external_cost_hash`,
      `retrieved_count`), making 10 total.
    - Type coercion matters: `cost_pence`, `budget_pence`, `started_at`, and `retrieved_count`
      are coerced to numbers exactly as JavaScript's `Number(x)` would (whole values serialize
      without a decimal point, fractional values keep their precision). `completed_at` is
      coerced to a string exactly as JavaScript's `String(x)` would. Getting this wrong
      produces a different canonical string and a different hash, and verification fails.

    **Canonicalization**: the signable object's keys are sorted alphabetically, then serialized
    as compact JSON with no extra whitespace -- equivalent to JavaScript's
    `JSON.stringify(obj, Object.keys(obj).sort())`. This exact behavior was cross-tested
    byte-for-byte across JavaScript, Python, and Go before any SDK trusted it.

    **Digest and verification**: SHA-256 hash the canonical string (as hex), then verify the
    proof's `signature` (base64-encoded) against that digest's raw bytes, using the Ed25519
    public key fetched from `/v1/workforce/proof/public-key`.

    ## Invoke lifecycle

    `POST /v1/agents/{slug}/invoke` enqueues a task and returns immediately with a `task_id`.
    It does not wait for completion. Poll `GET /v1/agents/{slug}/result/{taskId}` to check
    status. All three official SDKs use the same real polling behavior: starting at a 2500ms
    interval, increasing by 1000ms after each attempt, capped at 6000ms, for a caller-bounded
    total wait (default 60s, minimum 5s, maximum 120s). On timeout, the task is **not**
    re-invoked (that would risk double-charging) -- the caller receives the `task_id` back to
    poll again later.

    ## A genuine gap, stated honestly rather than guessed at

    The exact behavior of `GET /v1/workforce/proof/{task_id}/public` for a task that has not
    yet completed (whether it returns `404`, a `200` with a null proof, or something else) has
    not been directly tested tonight -- every real verification performed used a task that had
    already completed. This spec does not assert a specific behavior for that case; see the
    endpoint's own description for what's actually confirmed versus open.
  license:
    name: MIT
servers:
  - url: https://api.forcedream.ai
    description: Production (the only environment tested)

paths:
  /api/signup:
    post:
      operationId: signup
      summary: Create a new ForceDream account
      description: |
        No API key required -- this is how you get one. Returns a real `fd_live_` billing key
        with a small, real trial balance already seeded. Verified live, repeatedly, across all
        three SDKs tonight.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email:
                  type: string
                  format: email
                marketing_consent:
                  type: boolean
                  default: false
                  description: >-
                    Explicit opt-in only. Defaults to false -- an email address existing
                    because someone signed up is never treated as consent to be contacted.
      responses:
        '201':
          description: Account created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignupResult'

  /v1/account/balance:
    get:
      operationId: getBalance
      summary: Get the real, current account balance
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Current balance
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BalanceResult'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/agents/list:
    get:
      operationId: listAgents
      summary: Discover real ForceDream agents
      description: |
        Keyless -- no account needed. Real, load-bearing fact confirmed directly from the
        source, not assumed: **this endpoint has no working server-side capability or query
        filter.** All three official SDKs fetch the full list and filter client-side. A prior
        draft of the OpenAPI scope for this spec listed a nonexistent `/v1/agents/search`
        endpoint; it does not exist. Filter client-side against this endpoint's full response
        instead, exactly as the official SDKs do.
      responses:
        '200':
          description: Full agent registry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentListResult'

  /v1/agents/reliability:
    get:
      operationId: getAgentReliability
      summary: Real, system-measured reliability per agent
      description: >-
        Keyless. Used by all three SDKs to merge live `health` data into agent search results.
        A reliability-fetch failure never blocks the core agent listing.
      responses:
        '200':
          description: Reliability data for all agents
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReliabilityListResult'

  /v1/agents/{slug}/invoke:
    post:
      operationId: invokeAgent
      summary: Invoke a real agent to do real work (enqueues only)
      description: >-
        Spends your balance -- requires an `fd_live_` key. Enqueues the task and returns
        immediately; it does not wait for completion. Poll
        `/v1/agents/{slug}/result/{taskId}` for the outcome. Never call this again for the
        same logical task after a timeout -- re-invoking would double-charge.
      security:
        - bearerAuth: []
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
          example: data-extract-v1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [task]
              properties:
                task:
                  type: string
      responses:
        '200':
          description: Task enqueued
          content:
            application/json:
              schema:
                type: object
                properties:
                  task_id:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/agents/{slug}/result/{taskId}:
    get:
      operationId: getInvokeResult
      summary: Poll for the real result of an enqueued invocation
      description: |
        Real polling contract used identically by all three official SDKs: start polling at a
        2500ms interval, add 1000ms after each attempt, cap at 6000ms, bounded by a caller-set
        total wait (default 60s, min 5s, max 120s). The response's `status` (or `outcome`)
        field distinguishes `completed`/`succeeded`, `insufficient` (agent honestly declined --
        charged nothing), `charge_failed`, `failed`/`dead_letter`, or still-pending (any other
        value, including the field being absent).
      security:
        - bearerAuth: []
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
        - name: taskId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Current task state (may still be pending)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvokeResult'

  /v1/workforce/proof/public-key:
    get:
      operationId: getProofPublicKey
      summary: Fetch the real Ed25519 public key used to sign all proofs
      description: Keyless. Required to verify any proof's signature.
      responses:
        '200':
          description: Public key
          content:
            application/json:
              schema:
                type: object
                properties:
                  public_key_pem:
                    type: string
                  key_id:
                    type: string

  /v1/workforce/proof/{task_id}/public:
    get:
      operationId: getProof
      summary: Fetch a real, signed proof for a completed task
      description: >-
        Keyless. Returns the real proof object needed for client-side Ed25519 verification.
        **Honest gap**: the exact behavior for a task_id that has not yet completed (404,
        a 200 with a null proof, or otherwise) is not directly confirmed by tonight's testing
        -- every real verification tested used an already-completed task. Documented as `200`
        below for the confirmed case only; do not assume the shape of an unconfirmed case.
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Proof found (confirmed only for already-completed tasks)
          content:
            application/json:
              schema:
                type: object
                properties:
                  proof:
                    $ref: '#/components/schemas/FdProof'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An `fd_live_` billing key (from signup) or `sk_fd_` account key.

  responses:
    Unauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: Invalid API key (401).

  schemas:
    SignupResult:
      type: object
      properties:
        api_key: { type: string }
        user_id: { type: string }
        live_key: { type: string }
        trial_balance_pence: { type: integer }
        trial_balance_gbp: { type: string }
        referral_code: { type: string }
        message: { type: string }

    BalanceResult:
      type: object
      properties:
        user_id: { type: string }
        balance:
          type: object
          properties:
            pence: { type: integer }
            gbp: { type: string }
            withdrawable: { type: boolean }
        total_calls: { type: integer }
        earnings_pct: { type: integer }

    AgentMetrics:
      type: object
      description: System-derived from proofs/ledger. Never self-reported.
      properties:
        proof_count: { type: integer }
        tasks_completed: { type: integer }
        tasks_attempted: { type: integer }
        success_rate: { type: number }
        revenue_earned_pence: { type: integer }
        avg_cost_pence: { type: number }

    AgentReliability:
      type: object
      nullable: true
      description: Honestly null where no real reliability data exists yet.
      properties:
        success_rate: { type: number, nullable: true }
        avg_latency_ms: { type: number, nullable: true }
        sample_size: { type: integer }
        note: { type: string, nullable: true }

    Agent:
      type: object
      properties:
        slug: { type: string }
        name: { type: string }
        description: { type: string }
        version: { type: string }
        capabilities:
          type: array
          items: { type: string }
        price_per_call_pence: { type: integer }
        metrics:
          $ref: '#/components/schemas/AgentMetrics'
        health:
          $ref: '#/components/schemas/AgentReliability'

    AgentListResult:
      type: object
      properties:
        count: { type: integer }
        agents:
          type: array
          items:
            $ref: '#/components/schemas/Agent'
        note: { type: string }

    ReliabilityListResult:
      type: object
      properties:
        agents:
          type: array
          items:
            type: object
            properties:
              agent_slug: { type: string }
              reliability:
                $ref: '#/components/schemas/AgentReliability'

    InvokeResult:
      type: object
      properties:
        status:
          type: string
          enum: [completed, insufficient, pending, error]
        agent: { type: string }
        task_id: { type: string }
        output: {}
        charged_pence: { type: integer, nullable: true }
        proof_id: { type: string }
        error: { type: string }
        message: { type: string }

    FdProof:
      type: object
      description: >-
        The exact fields signed and verified. See x-forcedream-canonicalization for the
        precise reduced "signable" object this maps to -- it is not simply this whole object.
      required: [task_id, agent_id, input_hash, output_hash, cost_pence, budget_pence, started_at, completed_at]
      properties:
        task_id: { type: string }
        agent_id: { type: string }
        input_hash: { type: string }
        output_hash: { type: string }
        cost_pence:
          description: Real-world value is numeric but may arrive as a JSON string; coerce with Number(x) semantics before canonicalizing.
          oneOf: [{ type: number }, { type: string }]
        budget_pence:
          oneOf: [{ type: number }, { type: string }]
        external_cost_hash:
          type: string
          nullable: true
          description: When present, the signable includes 10 fields instead of 8.
        retrieved_count:
          oneOf: [{ type: number }, { type: string }]
          nullable: true
        started_at:
          oneOf: [{ type: number }, { type: string }]
        completed_at:
          description: Coerced to a string exactly as JS's String(x) would, not left as a raw number.
          oneOf: [{ type: number }, { type: string }]
        algorithm: { type: string, example: Ed25519 }
        signature:
          type: string
          description: Base64-encoded Ed25519 signature over the SHA-256 digest of the canonical signable string.
        key_id: { type: string }
        worm_seal: { type: string }
        proof_id: { type: string }

    VerifyResult:
      type: object
      properties:
        verified: { type: boolean }
        task_id: { type: string }
        key_id: { type: string }
        algorithm: { type: string, example: Ed25519 }
        fields_signed:
          type: integer
          enum: [8, 10]
          description: 8 for proofs without external_cost_hash, 10 for proofs with it.
        trustless: { type: boolean, enum: [true] }
        message: { type: string }
        note: { type: string }

x-forcedream-canonicalization:
  description: >-
    Non-standard OpenAPI extension documenting the exact, real canonicalization algorithm,
    since OpenAPI itself has no native way to express this. Cross-tested byte-for-byte and
    hash-for-hash across JavaScript, Python, and Go before any SDK trusted it -- not assumed.
  algorithm:
    step_1_build_signable: >-
      From an FdProof, construct an object with keys task_id, agent_id, input_hash,
      output_hash, cost_pence (Number-coerced), budget_pence (Number-coerced), started_at
      (Number-coerced), completed_at (String-coerced). If external_cost_hash is present
      (non-null), also add external_cost_hash (String-coerced) and retrieved_count
      (Number-coerced, defaulting to 0) -- 10 fields total instead of 8.
    step_2_canonicalize: >-
      Sort the signable object's keys alphabetically, then serialize as compact JSON with
      no whitespace after ':' or ','. Equivalent to JavaScript's
      JSON.stringify(obj, Object.keys(obj).sort()).
    step_3_digest: SHA-256 hash the canonical string, encoded as lowercase hex.
    step_4_verify: >-
      Base64-decode the proof's signature field. Hex-decode the digest to raw bytes. Verify
      the Ed25519 signature (raw bytes of the digest as the message) against the public key
      from GET /v1/workforce/proof/public-key.
  number_coercion_note: >-
    "Number-coerced" must match JavaScript's Number(x) -> JSON.stringify behavior exactly:
    whole-valued numbers serialize without a decimal point; fractional values keep full
    precision. A naive language-native int() cast (as first attempted in the Python SDK
    build) truncates fractional values and silently breaks verification. A naive %v-style
    generic formatter (as first attempted in the Go SDK build) can produce scientific
    notation for large values and silently breaks verification. Both were caught only by
    direct cross-language digest comparison, not by inspection.