> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hookie.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a workflow definition



## OpenAPI

````yaml /openapi.json post /admin/api/projects/{project_id}/workflows
openapi: 3.1.0
info:
  title: Hookie API
  version: 1.0.0
  summary: Capture, route and deliver webhooks.
  description: >-
    Hookie exposes three HTTP surfaces.


    **Ingest + streaming** is the public plane. A path-form endpoint URL
    authenticates by its own high-entropy slug — there is no key, token or
    cookie — and an `ik_live_…` ingest key authenticates the `/v1/*` routes.
    Whenever the URL names a dataset the payload is stored whole into it;
    project mapping rules are evaluated on exactly one shape, `POST
    /v1/ingest/{ingest_key}` with no dataset segment.


    **The customer portal API** is a separate, token-authed surface for your end
    customers, scoped to one portal and one customer.


    **The admin API** drives the console. It accepts either a browser session or
    an OAuth bearer token from a connected coding agent — the same routes, the
    same validation, the same audit trail. An agent's authority is its user's
    workspace role narrowed to the scopes granted in the console.


    This document is generated from the request handlers and adversarially
    verified against them.
  contact:
    name: Hookie
    url: https://hookie.ai
  license:
    name: Proprietary
    url: https://hookie.ai/legal/terms
servers:
  - url: https://app.hookie.ai
    description: Production
  - url: https://app.preview.hookie.ai
    description: Preview
security: []
tags:
  - name: Ingest
    description: Send events to Hookie.
  - name: Streaming
    description: Tail events in real time over SSE or WebSocket.
  - name: Portal
    description: Token-authed surface for your end customers.
  - name: Projects
    description: Projects and their settings.
  - name: Routing
    description: Rules, endpoints, datasets and records.
  - name: Delivery
    description: Destinations, deliveries and replay.
  - name: Workflows
    description: Multi-step workflows, triggers and AI agents.
  - name: Observability
    description: Search, correlation, stats and the audit log.
  - name: Workspace
    description: Members, connected agents, SSO and billing.
paths:
  /admin/api/projects/{project_id}/workflows:
    post:
      tags:
        - Workflows
      summary: Create a workflow definition
      operationId: createWorkflow
      parameters:
        - name: project_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - entry_dataset
                - steps
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                  description: Trimmed before validation.
                slug:
                  type: string
                  description: >-
                    Slugified server-side: lowercased, non-alphanumerics
                    collapsed to '-', trimmed, max 48 chars, falling back to
                    'workflow'. Defaults to a slug of `name`. Not
                    uniqueness-checked.
                active:
                  type: boolean
                  default: true
                  description: >-
                    Only the literal false deactivates; anything else (including
                    omission) means active. An inactive workflow starts no
                    instances.
                entry_dataset:
                  type: string
                  pattern: ^[a-zA-Z][a-zA-Z0-9_]{0,62}$
                  description: >-
                    A record landing in this dataset via the ingest/cron/ws
                    pipeline can start an instance. Records emitted BY a
                    workflow never start new ones.
                entry_conditions:
                  type: array
                  default: []
                  description: >-
                    Evaluated against the entry record. An empty array matches
                    every record in the dataset.
                  items:
                    type: object
                    required:
                      - path
                      - op
                    properties:
                      path:
                        type: string
                        minLength: 1
                        description: Dot path; the empty path is rejected.
                      op:
                        type: string
                        enum:
                          - equals
                          - not_equals
                          - contains
                          - exists
                          - gt
                          - lt
                      value:
                        description: >-
                          equals/not_equals use strict ===/!==. contains
                          requires both the resolved value and `value` to be
                          strings. gt/lt require both to be numbers. exists
                          ignores it (passes when the value is neither undefined
                          nor null).
                steps:
                  type: array
                  minItems: 1
                  description: >-
                    Ordered steps. Total count INCLUDING nested branch sub-steps
                    must be <= 32.
                  items:
                    oneOf:
                      - title: emit_event
                        type: object
                        required:
                          - type
                          - dataset
                        properties:
                          type:
                            type: string
                            enum:
                              - emit_event
                          dataset:
                            type: string
                            pattern: ^[a-zA-Z][a-zA-Z0-9_]{0,62}$
                          payload:
                            type: object
                            description: >-
                              Must be a plain object (not an array/null). Merged
                              OVER the instance context's entry fields — payload
                              wins.
                          deliver:
                            type: boolean
                            default: true
                            description: >-
                              Enqueue outbound deliveries for the emitted
                              record. Only the literal false disables it.
                      - title: call_ai
                        type: object
                        required:
                          - type
                        properties:
                          type:
                            type: string
                            enum:
                              - call_ai
                          instructions:
                            type: string
                            description: >-
                              Required — but `prompt` is accepted as an alias.
                              If both are absent or empty the step is rejected.
                          prompt:
                            type: string
                            description: >-
                              Alias for `instructions`; normalized into
                              `instructions` on write.
                          model:
                            type: string
                            description: >-
                              Workers AI model id. SILENTLY DROPPED unless it
                              starts with '@'; the run then uses the default
                              model.
                          max_tokens:
                            type: integer
                            description: Clamped to 1..1024. A non-integer is dropped.
                          output_key:
                            type: string
                            default: ai_result
                            description: Instance-context key the AI text is stored under.
                          output_dataset:
                            type: string
                            pattern: ^[a-zA-Z][a-zA-Z0-9_]{0,62}$
                            description: Optional dataset to also write the AI result into.
                      - title: wait_for_event
                        type: object
                        required:
                          - type
                          - dataset
                        properties:
                          type:
                            type: string
                            enum:
                              - wait_for_event
                          dataset:
                            type: string
                            pattern: ^[a-zA-Z][a-zA-Z0-9_]{0,62}$
                          conditions:
                            type: array
                            default: []
                            items:
                              type: object
                              required:
                                - path
                                - op
                              properties:
                                path:
                                  type: string
                                op:
                                  type: string
                                  enum:
                                    - equals
                                    - not_equals
                                    - contains
                                    - exists
                                    - gt
                                    - lt
                                value: {}
                          timeout_seconds:
                            type: integer
                            minimum: 1
                            description: >-
                              Must be > 0; floored. Timeouts are swept every ~5
                              minutes, so resolution is coarse.
                        description: NOT allowed inside a branch.
                      - title: delay
                        type: object
                        required:
                          - type
                          - seconds
                        properties:
                          type:
                            type: string
                            enum:
                              - delay
                          seconds:
                            type: integer
                            minimum: 1
                            description: >-
                              Must be > 0; floored. Resolution is bounded to ~5
                              minutes by the sweep.
                        description: NOT allowed inside a branch.
                      - title: agent_call
                        type: object
                        required:
                          - type
                          - agent_id
                        properties:
                          type:
                            type: string
                            enum:
                              - agent_call
                          agent_id:
                            type: string
                            minLength: 1
                            description: >-
                              Id of a project-scoped ai_agent. Existence is NOT
                              checked at write time — a missing or inactive
                              agent fails the step at run time.
                          output_key:
                            type: string
                            default: agent_result
                      - title: branch
                        type: object
                        required:
                          - type
                          - conditions
                          - then
                        properties:
                          type:
                            type: string
                            enum:
                              - branch
                          conditions:
                            type: array
                            minItems: 1
                            description: >-
                              At least one condition is required. Evaluated
                              against the instance CONTEXT (entry payload merged
                              with prior step outputs), so an earlier
                              call_ai/agent_call output_key can be branched on.
                            items:
                              type: object
                              required:
                                - path
                                - op
                              properties:
                                path:
                                  type: string
                                op:
                                  type: string
                                  enum:
                                    - equals
                                    - not_equals
                                    - contains
                                    - exists
                                    - gt
                                    - lt
                                value: {}
                          then:
                            type: array
                            minItems: 1
                            description: >-
                              Sub-steps run when the conditions match. Only
                              SYNCHRONOUS types allowed: emit_event, call_ai,
                              agent_call, branch.
                          else:
                            type: array
                            description: >-
                              Optional. Same restrictions as `then`. An empty
                              array is normalized away to absent.
                        description: Max nesting depth 3.
      responses:
        '201':
          description: Created.
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                  - ok
                properties:
                  id:
                    type: string
                  ok:
                    type: boolean
                    enum:
                      - true
        '400':
          description: >-
            Validation failure, one message. Examples: `body must be an object`;
            `name is required (<=120 chars)`; `entry_dataset: <dataset name
            error>`; `conditions must be an array`; `condition.path is
            required`; `condition.op must be one of
            equals|not_equals|contains|exists|gt|lt`; `steps must be an array`;
            `at least one step is required`; `too many steps (max 32 including
            branch sub-steps)`; `step N: unknown type "x"`; `step N (call_ai):
            instructions/prompt is required`; `step N (delay): seconds must be >
            0`; `step N (wait_for_event): not allowed inside a branch (branches
            run atomically)`; `step N (branch): max nesting depth is 3`; `step N
            (branch): then must contain at least one step`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Not signed in.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: >-
            `Insufficient role` (write capability required), or `Missing
            X-Requested-With header`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: '`Project not found`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: '`Internal error`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - session: []
        - bearerAuth: []
components:
  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Human-readable reason.
        retry_after:
          type: integer
          description: Seconds to wait. Present on 429.
        upgrade_url:
          type: string
          format: uri
          description: Present only on a monthly-quota 429.
  securitySchemes:
    session:
      type: apiKey
      in: cookie
      name: hookie_session
      description: >-
        The console's sealed session cookie, set by WorkOS AuthKit. Mutating
        requests also require the `X-Requested-With` CSRF header.
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        An OAuth 2.1 access token from a connected agent, audienced at the
        `/mcp` resource URI. The agent acts as its user, with that user's role
        narrowed to the granted `hookie:*` scopes. No CSRF header is required —
        a bearer token is not ambient credentials.

````