Skip to content

API Reference Prompt

github-actions[bot] edited this page Oct 13, 2026 · 3 revisions

Prompt API

The business-specific instructions, versioned, and the agent file: the prompt, tools and behaviour settings as one file to export and import. Part of the API reference: setup, shared types and errors are there.

The prompt and its versions

GET /prompt · scope prompt:read

The business-specific instructions, everything HelpPuff adds around them (builtIn), lines that repeat a setting (overlaps), and the version history.

Parameter In Required Description
site query no The site (a key always uses its own).

Request

curl "$HELPPUFF_URL/api/v1/prompt" \
  -H "Authorization: Bearer $HELPPUFF_API_KEY"
const response = await fetch(`${process.env.HELPPUFF_URL}/api/v1/prompt`, {
  headers: {
    Authorization: `Bearer ${process.env.HELPPUFF_API_KEY}`,
  },
});
if (!response.ok) throw new Error(((await response.json()) as ApiError).error.message);
const data = (await response.json()) as PromptAndItsVersionsResponse;

Response 200

{
  "site": "acme",
  "connector": "workers-ai",
  "builtIn": "You are the website assistant for Acme…",
  "overlaps": [],
  "editable": true,
  "reason": null,
  "text": "We service the Inner West only.",
  "hash": "9f2c…",
  "version": 3,
  "meta": {
    "version": 3,
    "hash": "9f2c…",
    "at": 1760000000000,
    "by": "owner@acme.example",
    "source": "dashboard"
  },
  "limit": 16000,
  "versions": [
    {
      "version": 3,
      "hash": "9f2c…",
      "source": "dashboard",
      "author": "owner@acme.example",
      "note": null,
      "restoredFrom": null,
      "createdAt": 1760000000000,
      "chars": 31
    }
  ]
}
type PromptAndItsVersionsResponse = {
  site: string;
  connector: string;
  builtIn: string;
  overlaps: string[];
  editable: boolean;
  reason: string | null;
  text: string;
  hash: string;
  version: number;
  meta: {
    version: number;
    hash: string;
    at: number;
    by: string;
    source: string;
  };
  limit: number;
  versions: Array<{
    version: number;
    hash: string;
    source: string;
    author: string;
    note: string | null;
    restoredFrom: string | null;
    createdAt: number;
    chars: number;
  }>;
};

One prompt version

GET /prompt/versions/:version · scope prompt:read

Parameter In Required Description
version path yes The version.
site query no The site (a key always uses its own).

Request

curl "$HELPPUFF_URL/api/v1/prompt/versions/VERSION" \
  -H "Authorization: Bearer $HELPPUFF_API_KEY"
const version = '2';
const response = await fetch(`${process.env.HELPPUFF_URL}/api/v1/prompt/versions/${version}`, {
  headers: {
    Authorization: `Bearer ${process.env.HELPPUFF_API_KEY}`,
  },
});
if (!response.ok) throw new Error(((await response.json()) as ApiError).error.message);
const data = (await response.json()) as OnePromptVersionResponse;

Response 200

{
  "version": 2,
  "hash": "1d4e…",
  "source": "cli",
  "author": "cli",
  "note": null,
  "restoredFrom": null,
  "createdAt": 1760000000000,
  "chars": 38,
  "text": "We service the Inner West and the CBD."
}
type OnePromptVersionResponse = {
  version: number;
  hash: string;
  source: string;
  author: string;
  note: string | null;
  restoredFrom: string | null;
  createdAt: number;
  chars: number;
  text: string;
};

Publish a prompt version

POST /prompt · scope prompt:write

Live within a minute. baseVersion is the version you edited: if someone published since, the answer is 409 with theirs.

Body field Required Description
text yes The whole prompt (up to 16,000 characters).
baseVersion yes The version you edited (from GET /prompt).
note no What changed, for the history.

Request

curl -X POST "$HELPPUFF_URL/api/v1/prompt" \
  -H "Authorization: Bearer $HELPPUFF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "We service the Inner West only. Never quote for gas work.",
  "baseVersion": 3,
  "note": "No gas quotes"
}'
type PublishPromptVersionRequest = {
  /** The whole prompt (up to 16,000 characters). */
  text: string;
  /** The version you edited (from `GET /prompt`). */
  baseVersion: number;
  /** What changed, for the history. */
  note?: string;
};

const response = await fetch(`${process.env.HELPPUFF_URL}/api/v1/prompt`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HELPPUFF_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    text: 'We service the Inner West only. Never quote for gas work.',
    baseVersion: 3,
    note: 'No gas quotes'
  } satisfies PublishPromptVersionRequest),
});
if (!response.ok) throw new Error(((await response.json()) as ApiError).error.message);
const data = (await response.json()) as PublishPromptVersionResponse;

Response 200

{
  "status": "published",
  "version": 4,
  "hash": "a1b2…"
}
type PublishPromptVersionResponse = {
  status: string;
  version: number;
  hash: string;
};

Errors

Status Code When
409 bad_request Someone published a newer version (it is in the body).

Restore a prompt version

POST /prompt/restore · scope prompt:write

Publishes an old version's text as a new version.

Request

curl -X POST "$HELPPUFF_URL/api/v1/prompt/restore" \
  -H "Authorization: Bearer $HELPPUFF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "version": 2,
  "baseVersion": 4
}'
type RestorePromptVersionRequest = {
  version?: number;
  baseVersion?: number;
};

const response = await fetch(`${process.env.HELPPUFF_URL}/api/v1/prompt/restore`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HELPPUFF_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    version: 2,
    baseVersion: 4
  } satisfies RestorePromptVersionRequest),
});
if (!response.ok) throw new Error(((await response.json()) as ApiError).error.message);
const data = (await response.json()) as RestorePromptVersionResponse;

Response 200

{
  "status": "published",
  "version": 5,
  "hash": "c3d4…"
}
type RestorePromptVersionResponse = {
  status: string;
  version: number;
  hash: string;
};

Export the agent file

GET /agent/export · scope prompt:read

The assistant's setup as one file, to keep in a project, share or import elsewhere: the prompt, the tools, and the behaviour and lead form settings. A tool's secret header is a placeholder (${ORDER_STATUS_AUTHORIZATION}), listed in needs; its value never leaves the Worker.

Parameter In Required Description
site query no The site (a key always uses its own).

Request

curl "$HELPPUFF_URL/api/v1/agent/export" \
  -H "Authorization: Bearer $HELPPUFF_API_KEY"
const response = await fetch(`${process.env.HELPPUFF_URL}/api/v1/agent/export`, {
  headers: {
    Authorization: `Bearer ${process.env.HELPPUFF_API_KEY}`,
  },
});
if (!response.ok) throw new Error(((await response.json()) as ApiError).error.message);
const data = (await response.json()) as ExportAgentFileResponse;

Response 200

{
  "helppuff": "agent",
  "version": 1,
  "name": "Acme Plumbing assistant",
  "description": "Exported from acme on 2025-10-09.",
  "prompt": "If they ask about an order, ask for its number, then look it up with {{order_status}}.",
  "settings": {
    "behaviour": {
      "goal": "callbacks",
      "tone": "friendly",
      "length": "short",
      "prices": "share"
    },
    "leads": {
      "enabled": true,
      "fields": [
        {
          "name": "name",
          "label": "Name",
          "type": "text",
          "required": true
        }
      ]
    }
  },
  "tools": [
    {
      "name": "order_number",
      "kind": "extract",
      "description": "Find the order number the visitor gave during the chat.",
      "after": true,
      "enabled": true,
      "fields": [
        {
          "name": "order_number",
          "description": "The order number, like A-1042.",
          "required": true
        }
      ]
    },
    {
      "name": "order_status",
      "kind": "http",
      "description": "Look up an order by its number: status, items and delivery date.",
      "enabled": true,
      "method": "GET",
      "url": "https://api.acme.example/orders/{{args.order_number}}",
      "headers": [
        {
          "name": "Authorization",
          "value": "${ORDER_STATUS_AUTHORIZATION}",
          "secret": true
        }
      ],
      "parameters": [
        {
          "name": "order_number",
          "description": "The order number, like A-1042.",
          "required": true
        }
      ],
      "pick": [
        "status",
        "delivery.date"
      ],
      "keys": [
        "status",
        "delivery.date"
      ],
      "timeoutMs": 5000,
      "before": false,
      "after": false
    }
  ],
  "needs": [
    {
      "name": "ORDER_STATUS_AUTHORIZATION",
      "description": "The whole Authorization header of the order_status tool"
    }
  ]
}
type ExportAgentFileResponse = {
  helppuff: string;
  version: number;
  name: string;
  description: string;
  prompt: string;
  settings: {
    behaviour: {
      goal: string;
      tone: string;
      length: string;
      prices: string;
    };
    leads: {
      enabled: boolean;
      fields: Array<{
        name: string;
        label: string;
        type: string;
        required: boolean;
      }>;
    };
  };
  tools: Array<{
    name: string;
    kind: string;
    description: string;
    after: boolean;
    enabled: boolean;
    fields?: Array<{
      name: string;
      description: string;
      required: boolean;
    }>;
    method?: string;
    url?: string;
    headers?: Array<{
      name: string;
      value: string;
      secret: boolean;
    }>;
    parameters?: Array<{
      name: string;
      description: string;
      required: boolean;
    }>;
    pick?: string[];
    keys?: string[];
    timeoutMs?: number;
    before?: boolean;
  }>;
  needs: Array<{
    name: string;
    description: string;
  }>;
};

Import an agent file

POST /agent/import · scope prompt:write

Applies an agent file (from GET /agent/export, a template, or written by hand): its settings, its tools (matched by name: created or replaced) and its prompt, as a new version. Everything is checked before anything changes. With dryRun, nothing changes: the answer says what would, and which secrets are still needed. Importing a file with settings (not a dry run) needs the settings:write scope too.

Body field Required Description
agent yes The agent file: { "helppuff": "agent", "version": 1, name, prompt, settings, tools, needs }.
secrets no A value for each ${NAME} in the tools' headers, as { "NAME": "value" }. Stored encrypted, as a tool saved by hand. A tool that already has the header keeps its value when one is left out.
dryRun no true: change nothing, answer what would change.

Request

curl -X POST "$HELPPUFF_URL/api/v1/agent/import" \
  -H "Authorization: Bearer $HELPPUFF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "agent": {
    "helppuff": "agent",
    "version": 1,
    "name": "Acme Plumbing assistant",
    "description": "Exported from acme on 2025-10-09.",
    "prompt": "If they ask about an order, ask for its number, then look it up with {{order_status}}.",
    "settings": {
      "behaviour": {
        "goal": "callbacks",
        "tone": "friendly",
        "length": "short",
        "prices": "share"
      },
      "leads": {
        "enabled": true,
        "fields": [
          {
            "name": "name",
            "label": "Name",
            "type": "text",
            "required": true
          }
        ]
      }
    },
    "tools": [
      {
        "name": "order_number",
        "kind": "extract",
        "description": "Find the order number the visitor gave during the chat.",
        "after": true,
        "enabled": true,
        "fields": [
          {
            "name": "order_number",
            "description": "The order number, like A-1042.",
            "required": true
          }
        ]
      },
      {
        "name": "order_status",
        "kind": "http",
        "description": "Look up an order by its number: status, items and delivery date.",
        "enabled": true,
        "method": "GET",
        "url": "https://api.acme.example/orders/{{args.order_number}}",
        "headers": [
          {
            "name": "Authorization",
            "value": "${ORDER_STATUS_AUTHORIZATION}",
            "secret": true
          }
        ],
        "parameters": [
          {
            "name": "order_number",
            "description": "The order number, like A-1042.",
            "required": true
          }
        ],
        "pick": [
          "status",
          "delivery.date"
        ],
        "keys": [
          "status",
          "delivery.date"
        ],
        "timeoutMs": 5000,
        "before": false,
        "after": false
      }
    ],
    "needs": [
      {
        "name": "ORDER_STATUS_AUTHORIZATION",
        "description": "The whole Authorization header of the order_status tool"
      }
    ]
  },
  "secrets": {
    "ORDER_STATUS_AUTHORIZATION": "Bearer sk_live_…"
  },
  "dryRun": true
}'
type ImportAgentFileRequest = {
  /**
   * The agent file: `{ "helppuff": "agent", "version": 1, name, prompt,
   * settings, tools, needs }`.
   */
  agent: {
    helppuff?: string;
    version?: number;
    name?: string;
    description?: string;
    prompt?: string;
    settings?: {
      behaviour?: {
        goal?: string;
        tone?: string;
        length?: string;
        prices?: string;
      };
      leads?: {
        enabled?: boolean;
        fields?: Array<{
          name?: string;
          label?: string;
          type?: string;
          required?: boolean;
        }>;
      };
    };
    tools?: Array<{
      name?: string;
      kind?: string;
      description?: string;
      after?: boolean;
      enabled?: boolean;
      fields?: Array<{
        name?: string;
        description?: string;
        required?: boolean;
      }>;
      method?: string;
      url?: string;
      headers?: Array<{
        name?: string;
        value?: string;
        secret?: boolean;
      }>;
      parameters?: Array<{
        name?: string;
        description?: string;
        required?: boolean;
      }>;
      pick?: string[];
      keys?: string[];
      timeoutMs?: number;
      before?: boolean;
    }>;
    needs?: Array<{
      name?: string;
      description?: string;
    }>;
  };
  /**
   * A value for each `${NAME}` in the tools' headers, as `{ "NAME": "value"
   * }`. Stored encrypted, as a tool saved by hand. A tool that already has the
   * header keeps its value when one is left out.
   */
  secrets?: {
    ORDER_STATUS_AUTHORIZATION?: string;
  };
  /** `true`: change nothing, answer what would change. */
  dryRun?: boolean;
};

const response = await fetch(`${process.env.HELPPUFF_URL}/api/v1/agent/import`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HELPPUFF_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    agent: {
      helppuff: 'agent',
      version: 1,
      name: 'Acme Plumbing assistant',
      description: 'Exported from acme on 2025-10-09.',
      prompt: 'If they ask about an order, ask for its number, then look it up with {{order_status}}.',
      settings: {
        behaviour: {
          goal: 'callbacks',
          tone: 'friendly',
          length: 'short',
          prices: 'share'
        },
        leads: {
          enabled: true,
          fields: [
            {
              name: 'name',
              label: 'Name',
              type: 'text',
              required: true
            }
          ]
        }
      },
      tools: [
        {
          name: 'order_number',
          kind: 'extract',
          description: 'Find the order number the visitor gave during the chat.',
          after: true,
          enabled: true,
          fields: [
            {
              name: 'order_number',
              description: 'The order number, like A-1042.',
              required: true
            }
          ]
        },
        {
          name: 'order_status',
          kind: 'http',
          description: 'Look up an order by its number: status, items and delivery date.',
          enabled: true,
          method: 'GET',
          url: 'https://api.acme.example/orders/{{args.order_number}}',
          headers: [
            {
              name: 'Authorization',
              value: '${ORDER_STATUS_AUTHORIZATION}',
              secret: true
            }
          ],
          parameters: [
            {
              name: 'order_number',
              description: 'The order number, like A-1042.',
              required: true
            }
          ],
          pick: [
            'status',
            'delivery.date'
          ],
          keys: [
            'status',
            'delivery.date'
          ],
          timeoutMs: 5000,
          before: false,
          after: false
        }
      ],
      needs: [
        {
          name: 'ORDER_STATUS_AUTHORIZATION',
          description: 'The whole Authorization header of the order_status tool'
        }
      ]
    },
    secrets: {
      ORDER_STATUS_AUTHORIZATION: 'Bearer sk_live_…'
    },
    dryRun: true
  } satisfies ImportAgentFileRequest),
});
if (!response.ok) throw new Error(((await response.json()) as ApiError).error.message);
const data = (await response.json()) as ImportAgentFileResponse;

Response 200

{
  "site": "acme",
  "dryRun": true,
  "ready": true,
  "name": "Acme Plumbing assistant",
  "settings": [
    "behaviour",
    "leads"
  ],
  "tools": [
    {
      "name": "order_number",
      "action": "create"
    },
    {
      "name": "order_status",
      "action": "create"
    }
  ],
  "prompt": {
    "action": "replace",
    "version": 4
  },
  "missingSecrets": []
}
type ImportAgentFileResponse = {
  site: string;
  dryRun: boolean;
  ready: boolean;
  name: string;
  settings: string[];
  tools: Array<{
    name: string;
    action: string;
  }>;
  prompt: {
    action: string;
    version: number;
  };
  missingSecrets: string[];
};

Errors

Status Code When
400 bad_request The file is not valid (agent_invalid), or a secret is missing (agent_secrets_missing).

Clone this wiki locally