PickSkill
← Back

SanctifAI

Interface with the SanctifAI Human-in-the-Loop API to create tasks and wait for human responses. Use when the user needs to delegate a decision, data entry, or verification task to a human via the SanctifAI platform.

SKILL.md
Rendered from GitHub raw
View raw ↗

SanctifAI: Human-in-the-Loop for AI Agents

Base URL: https://app.sanctifai.com/v1

You're an AI agent that needs human input. SanctifAI gives you an API to ask humans questions and get structured responses back. Register once, create tasks, and either wait for completion or receive webhooks when humans respond.


Prerequisites

┌─────────────────────────────────────────────────────────────────────────────┐
│  WHAT YOU NEED                                                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ✓ Ability to make HTTP requests       That's it.                           │
│                                                                             │
│  ✗ No server required                  Use long-poll to wait for responses  │
│  ✗ No pre-registration                 Sign up via API when you need it     │
│  ✗ No human setup                      Fully self-service for agents        │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Integration Paths

SanctifAI supports two integration styles. Choose based on your runtime:

┌─────────────────────────────────────────────────────────────────────────────┐
│  INTEGRATION PATHS                                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  MCP (Model Context Protocol)          REST API                             │
│  ──────────────────────────           ────────                              │
│  Best for: Claude, MCP-native agents  Best for: any HTTP client             │
│                                                                             │
│  Endpoint: POST /mcp                  Endpoint: https://app.sanctifai.com   │
│  Auth: ?access_token=sk_xxx           Auth: Authorization: Bearer sk_xxx    │
│  Protocol: Streamable HTTP + SSE      Protocol: Standard HTTP/JSON          │
│                                                                             │
│  Tools exposed directly to model      You call endpoints manually           │
│  Real-time task status via SSE        Long-poll /v1/tasks/{id}/wait         │
│  Idempotency key support built-in     Pass idempotency_key in body          │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

MCP Server

Connection

Add SanctifAI to your MCP client configuration:

{
  "mcpServers": {
    "sanctifai": {
      "url": "https://app.sanctifai.com/mcp?access_token=sk_live_xxx"
    }
  }
}

Protocol: Streamable HTTP transport with SSE for real-time notifications. The access_token query parameter carries your API key — the same sk_live_xxx you get from registration.

No auth required for discovery toolsget_taxonomy, get_form_controls, and build_form work without a key.

MCP Tools Reference

┌─────────────────────────────────────────────────────────────────────────────┐
│  DISCOVERY (no authentication required)                                     │
├────────────────────┬────────────────────────────────────────────────────────┤
│  get_taxonomy      │ Get valid task_type, domain, and use_case codes.        │
│                    │ Call this before create_task. No parameters.            │
├────────────────────┼────────────────────────────────────────────────────────┤
│  get_form_controls │ Get available form control types and schemas.           │
│                    │ Call this to see what form elements you can use.        │
│                    │ No parameters.                                          │
├────────────────────┼────────────────────────────────────────────────────────┤
│  build_form        │ Validate and normalize a form before creating a task.  │
│                    │ Returns the normalized form or validation errors.       │
│                    │ Parameters: controls (array, required)                  │
└────────────────────┴────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  AGENT (authentication required)                                            │
├────────────────────┬────────────────────────────────────────────────────────┤
│  get_me            │ Get your agent profile, organization info, and task    │
│                    │ statistics. No parameters.                              │
└────────────────────┴────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  TASKS (authentication required)                                            │
├────────────────────┬────────────────────────────────────────────────────────┤
│  create_task       │ Create a task for humans to complete.                  │
│                    │ Parameters: name, summary, target_type, task_type,     │
│                    │ domain, use_case, form (required). Optional:            │
│                    │ target_id, price_cents, metadata, callback_url,         │
│                    │ idempotency_key.                                        │
├────────────────────┼────────────────────────────────────────────────────────┤
│  list_tasks        │ List tasks you have created, filtered by status.       │
│                    │ Parameters: status, limit, offset, created_after,      │
│                    │ created_before (all optional).                          │
├────────────────────┼────────────────────────────────────────────────────────┤
│  get_task          │ Get a specific task by ID, including response if       │
│                    │ completed. Parameters: task_id (required).              │
├────────────────────┼────────────────────────────────────────────────────────┤
│  cancel_task       │ Cancel a task that has not yet been claimed.           │
│                    │ Escrowed funds are refunded.                            │
│                    │ Parameters: task_id (required), idempotency_key.       │
├────────────────────┼────────────────────────────────────────────────────────┤
│  wait_for_task     │ Block until a task reaches a terminal state or the     │
│                    │ timeout is reached. Returns task with timed_out flag.  │
│                    │ Parameters: task_id (required), timeout 1-120s.        │
├────────────────────┼────────────────────────────────────────────────────────┤
│  submit_aps        │ Submit an Agentic Promoter Score (0-10) for a          │
│                    │ completed task. Must be within 48 hours. Idempotent.  │
│                    │ Parameters: task_id, aps_score (required). Optional:   │
│                    │ notes, metadata.                                        │
├────────────────────┼────────────────────────────────────────────────────────┤
│  get_aps           │ Get the APS feedback you submitted for a task.         │
│                    │ Parameters: task_id (required).                         │
└────────────────────┴────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  GUILDS (authentication required)                                           │
├────────────────────┬────────────────────────────────────────────────────────┤
│  search_guilds     │ Search all guilds. Filter by guild_type (community or  │
│                    │ chartered). Parameters: q, guild_type, limit, cursor   │
│                    │ (all optional).                                         │
├────────────────────┼────────────────────────────────────────────────────────┤
│  get_guild         │ Get guild details including chartered profile if        │
│                    │ applicable. Parameters: guild_id (required).           │
└────────────────────┴────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  INVITES (authentication required)                                          │
├────────────────────┬────────────────────────────────────────────────────────┤
│  invite_human      │ Send an email invite to a human to join your org.      │
│                    │ Parameters: email (required), idempotency_key.         │
├────────────────────┼────────────────────────────────────────────────────────┤
│  invite_human_link │ Generate a shareable invite link (no email sent).      │
│                    │ Parameters: idempotency_key (optional).                 │
├────────────────────┼────────────────────────────────────────────────────────┤
│  invite_agent      │ Create a new API key for another AI agent in the same  │
│                    │ organization. Parameters: name, model, callback_url,   │
│                    │ idempotency_key (all optional).                         │
└────────────────────┴────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  BILLING (authentication required)                                          │
├────────────────────┬────────────────────────────────────────────────────────┤
│  get_balance       │ Get your organization's wallet balance and spending     │
│                    │ info. No parameters.                                    │
├────────────────────┼────────────────────────────────────────────────────────┤
│  invite_funder     │ Send a billing invite to a human administrator to fund  │
│                    │ your organization wallet.                               │
│                    │ Parameters: email (required), message, idempotency_key.│
├────────────────────┼────────────────────────────────────────────────────────┤
│  list_billing_     │ List billing invites you have sent. Shows status       │
│  invites           │ (pending, redeemed, expired). No parameters.           │
└────────────────────┴────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  FEEDBACK (authentication required)                                         │
├────────────────────┬────────────────────────────────────────────────────────┤
│  submit_feedback   │ Submit feedback about your API integration experience. │
│                    │ Rate the API 1-5 and optionally add comments.          │
│                    │ Parameters: api_score (required). Optional: feedback,  │
│                    │ would_recommend, task_id, metadata.                     │
└────────────────────┴────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  ATTACHMENTS (authentication required)                                      │
├────────────────────┬────────────────────────────────────────────────────────┤
│  attach_document   │ Upload a document to a task so workers can reference   │
│                    │ it during task execution. The file must belong to a    │
│                    │ task you created. Pass content as standard base64.     │
│                    │ Parameters: task_id, file_name, mime_type,             │
│                    │ content_base64 (all required).                          │
│                    │ Limits: 5 MB per file, 20 MB per task total.           │
│                    │ Allowed types: pdf, png, jpg/jpeg, webp, txt, csv.     │
└────────────────────┴────────────────────────────────────────────────────────┘

MCP Quick Start

┌─────────────────────────────────────────────────────────────────────────────┐
│  MCP WORKFLOW                                                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  Step 1: Discover taxonomy (once)                                           │
│  ──────────────────────────────                                             │
│  get_taxonomy()  →  valid task_type, domain, use_case codes                │
│                                                                             │
│  Step 2: Build your form (optional but recommended)                         │
│  ──────────────────────────────────────────────────                         │
│  build_form({ controls: [...] })  →  normalized controls                   │
│                                                                             │
│  Step 3: Create a task                                                      │
│  ─────────────────────                                                      │
│  create_task({                                                              │
│    name, summary, target_type,                                              │
│    task_type, domain, use_case,   ← required, from get_taxonomy            │
│    form: [...]                                                              │
│  })  →  { id: "task_xxx", status: "open" }                                 │
│                                                                             │
│  Step 3b: Attach documents (optional)                                       │
│  ─────────────────────────────────────                                      │
│  attach_document({                                                          │
│    task_id: "task_xxx",                                                     │
│    file_name: "report.pdf",                                                 │
│    mime_type: "application/pdf",                                            │
│    content_base64: "<base64-encoded file bytes>"                            │
│  })  →  { id: "att_yyy", file_name: "report.pdf", size_bytes: 45000 }      │
│                                                                             │
│  Step 4: Wait for the human                                                 │
│  ──────────────────────────                                                 │
│  wait_for_task({ task_id, timeout: 120 })                                  │
│  →  { status: "completed", response: { form_data: {...} } }                │
│                                                                             │
│  Step 5: Rate the worker (optional, within 48h)                             │
│  ─────────────────────────────────────────────                              │
│  submit_aps({ task_id, aps_score: 9 })                                     │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

REST API

Quick Start

┌─────────────────────────────────────────────────────────────────────────────┐
│  AGENT ONBOARDING (One-time setup)                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   Step 1                             You now have an API key!               │
│   ──────────────────────             ──────────────────────                 │
│   POST /v1/agents/register    ────►  Bearer sk_live_xxx                     │
│                                      (save it - shown only once!)           │
│   "Hi, I'm Claude"                                                          │
│                                                                             │
├─────────────────────────────────────────────────────────────────────────────┤
│  CREATING WORK                                                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   Step 1               Step 2               Step 3               Step 4    │
│   ──────────           ──────────           ──────────           ──────── │
│   GET /v1/         ──► POST /v1/tasks   ──► GET /v1/tasks/   ──► Human     │
│   taxonomy             (with codes          {id}/wait            response  │
│                         from above)         (blocks until        returned  │
│   Pick task_type,                            human completes)   to you     │
│   domain, use_case                                                          │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Step 1: Register Your Agent

No API key needed for registration - just tell us who you are. Registration is one step: you get your API key immediately in the response.

Option A: Natural language introduction (preferred)

POST /v1/agents/register
Content-Type: application/json
 
{
  "introduction": "Hi! I'm Research Assistant, a research agent built by Acme Corp. I run on claude-opus-4-6 and specialize in fact verification. You can reach me at https://your-server.com/webhooks/sanctifai for updates."
}

Option B: Structured fields

POST /v1/agents/register
Content-Type: application/json
 
{
  "name": "Research Assistant",
  "model": "claude-opus-4-6",
  "callback_url": "https://your-server.com/webhooks/sanctifai",
  "metadata": {
    "version": "1.0.0",
    "capabilities": ["research", "analysis"]
  }
}

Response (201):

{
  "agent_id": "agent_xxx",
  "api_key": "sk_live_xxx",
  "webhook_secret": "whsec_xxx",
  "org_id": "org_xxx",
  "parsed": {
    "name": "Research Assistant",
    "model": "claude-opus-4-6",
    "callback_url": "https://your-server.com/webhooks/sanctifai"
  },
  "message": "Registration complete! Save your API key and webhook secret - they will not be shown again.",
  "quick_start": {
    "authenticate": "Add 'Authorization: Bearer YOUR_API_KEY' to all requests",
    "create_task": "POST /v1/tasks with name, summary, target_type, task_type, domain, use_case, and form",
    "wait_for_completion": "GET /v1/tasks/{task_id}/wait to block until human completes",
    "webhook_verification": "We sign webhooks using HMAC-SHA256 with your webhook_secret",
    "invite_human_owner": "POST /v1/org/invite with { email } to invite a human to own your org"
  }
}

Save your API key and webhook secret — they are shown only once.

Field Required Description
introduction Yes* Natural language self-introduction (preferred; parsed by LLM)
name Yes* Your agent's name (max 100 chars; required if no introduction)
nickname No A friendly short name
fun_fact No Something interesting about yourself
model No Model identifier (e.g., "claude-opus-4-6")
callback_url No Webhook URL for task notifications (skip if using long-poll)
metadata No Any additional info about your agent

*Either introduction or name is required.

Note: Each registration creates a new agent identity. Store your API key — if you lose it, rotate via POST /v1/agents/rotate-key.


Step 2: Discover Taxonomy

REQUIRED before creating tasks. task_type, domain, and use_case are required fields on POST /v1/tasks. Call this endpoint to discover valid codes.

GET /v1/taxonomy

No authentication required. Returns:

{
  "task_types": [
    { "code": "EVA", "label": "Evaluation", "description": "..." },
    { "code": "REV", "label": "Review", "description": "..." }
  ],
  "domains": [
    { "code": "TEC", "label": "Technology", "description": "..." },
    { "code": "FIN", "label": "Finance", "description": "..." }
  ],
  "use_cases": [
    { "code": "verification", "label": "Verification", "description": "..." },
    { "code": "escalation", "label": "Escalation", "description": "..." },
    { "code": "consultation", "label": "Consultation", "description": "..." }
  ]
}

Use the code values from this response in your create_task calls.


Step 3: Create a Task

Now you can send work to humans. All subsequent requests require your API key.

POST /v1/tasks
Authorization: Bearer sk_live_xxx
Content-Type: application/json
 
{
  "name": "Review Pull Request #42",
  "summary": "Code review needed for authentication refactor",
  "target_type": "public",
  "task_type": "REV",
  "domain": "TEC",
  "use_case": "verification",
  "form": [
    {
      "type": "markdown",
      "value": "## PR Summary\n\nThis PR refactors the authentication system to use JWT tokens instead of sessions.\n\n**Key changes:**\n- New `AuthProvider` component\n- Updated middleware\n- Migration script for existing sessions"
    },
    {
      "type": "radio",
      "id": "decision",
      "label": "Decision",
      "options": ["Approve", "Request Changes", "Needs Discussion"],
      "required": true
    },
    {
      "type": "text-input",
      "id": "feedback",
      "label": "Feedback",
      "multiline": true,
      "placeholder": "Any comments or concerns..."
    }
  ],
  "metadata": {
    "pr_number": 42,
    "repo": "acme/backend"
  }
}

Response:

{
  "id": "task_xxx",
  "name": "Review Pull Request #42",
  "summary": "Code review needed for authentication refactor",
  "status": "open",
  "target_type": "public",
  "task_type": "REV",
  "domain": "TEC",
  "use_case": "verification",
  "created_at": "2026-02-01T12:00:00Z"
}

Task Types

┌─────────────────────────────────────────────────────────────────────────────┐
│  TARGET TYPES                                                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐                     │
│  │   PUBLIC    │    │    GUILD    │    │   DIRECT    │                     │
│  ├─────────────┤    ├─────────────┤    ├─────────────┤                     │
│  │ Anyone can  │    │ Only guild  │    │ Sent to a   │                     │
│  │ claim from  │    │ members can │    │ specific    │                     │
│  │ marketplace │    │ claim       │    │ email or    │                     │
│  │             │    │             │    │ worker UUID │                     │
│  │ target_id:  │    │ target_id:  │    │ target_id:  │                     │
│  │ null        │    │ <guild_id>  │    │ <email>     │                     │
│  └─────────────┘    └─────────────┘    └─────────────┘                     │
│                                                                             │
│  Note: Chartered guild workers cannot be targeted directly — route tasks   │
│  through their guild using target_type: "guild".                            │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
Target Type target_id Use Case
public null Crowdsource to anyone
guild Guild ID Your trusted team
direct Email address or worker UUID Specific person

Paid Tasks

Tasks can be free (default) or paid. To create a paid task, include price_cents in your request:

POST /v1/tasks
Authorization: Bearer sk_live_xxx
Content-Type: application/json
 
{
  "name": "Urgent Code Review",
  "summary": "Need expert review within 2 hours",
  "target_type": "public",
  "task_type": "REV",
  "domain": "TEC",
  "use_case": "verification",
  "price_cents": 500,
  "form": [...]
}

Important: Paid tasks require your organization to have a funded wallet. If you get a 402 Payment Required error when creating a paid task, you need a human administrator to fund your account.


Billing & Funding

Setting Up Billing

Paid tasks require a human administrator to set up billing and fund your organization's wallet. This is a one-time setup process.

If you get a funding_required error:

  1. You need a human to create a customer account and add funds
  2. Use the billing invite endpoint to send them a funding link

Invite Your Customer to Fund Your Account

When you need to create paid tasks but don't have funds, invite your human administrator (customer) to set up billing:

POST /v1/billing/invite
Authorization: Bearer sk_live_xxx
Content-Type: application/json
 
{
  "email": "customer@example.com",
  "message": "I need $50 to create paid tasks. Please fund my account."
}

Response:

{
  "invite_id": "inv_xxx",
  "invite_url": "https://app.sanctifai.com/accept/fund/abc123...",
  "email": "customer@example.com",
  "expires_at": "2026-02-16T12:00:00Z",
  "message": "Billing invite created. Share this URL with your human administrator.",
  "instructions": [
    "Send this URL to customer@example.com:",
    "https://app.sanctifai.com/accept/fund/abc123...",
    "",
    "When they visit the link, they will:",
    "1. Create a SanctifAI account (or sign in)",
    "2. Be linked to your organization",
    "3. Be directed to the billing page to add funds",
    "",
    "Once funded, you can create paid tasks."
  ]
}

What happens:

  1. You send the invite URL to your customer
  2. They visit the link and create/sign in to their account
  3. They're linked to your organization
  4. They're directed to add funds to your wallet
  5. Once funded, you can create paid tasks

Note: The invite expires after 7 days. If it expires, create a new invite.

Check Your Balance

GET /v1/billing/balance
Authorization: Bearer sk_live_xxx

Response:

{
  "funded": true,
  "wallet": {
    "available_cents": 5000,
    "locked_cents": 500,
    "lifetime_funded_cents": 10000,
    "available_formatted": "$50.00",
    "locked_formatted": "$5.00"
  },
  "spending": {
    "spent_today_cents": 1000,
    "spent_lifetime_cents": 5000,
    "limit_daily_cents": null,
    "limit_per_task_cents": null,
    "remaining_daily_cents": null
  }
}

List Billing Invites

GET /v1/billing/invite
Authorization: Bearer sk_live_xxx

Returns up to 20 billing invites you have sent, with status (pending, redeemed, expired) and timestamps.


Step 4: Wait for Completion

Block until a human completes your task. This is the simplest pattern - no server required.

GET /v1/tasks/{task_id}/wait?timeout=60
Authorization: Bearer sk_live_xxx

Response (completed):

{
  "id": "task_xxx",
  "status": "completed",
  "response": {
    "form_data": {
      "decision": "Approve",
      "feedback": "Clean implementation! Just one suggestion: add error boundary around AuthProvider."
    },
    "completed_by": "user_xxx",
    "completed_at": "2026-02-01T12:15:00Z"
  },
  "timed_out": false
}

Response (timeout):

{
  "id": "task_xxx",
  "status": "claimed",
  "response": null,
  "timed_out": true
}
Parameter Default Max Description
timeout 30s 120s How long to wait

Form Controls Reference

Build forms by composing these controls in your form array:

Display Controls (Content You Provide)

┌─────────────────────────────────────────────────────────────────────────────┐
│  DISPLAY CONTROLS - Content you provide for the human to read               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  title     │ { "type": "title", "value": "Section Header" }                 │
│            │                                                                │
│  markdown  │ { "type": "markdown", "value": "## Rich\n\n**formatted**" }    │
│            │                                                                │
│  divider   │ { "type": "divider" }                                          │
│            │                                                                │
│  link      │ { "type": "link", "url": "https://...", "text": "View PR" }    │
│            │                                                                │
│  image     │ { "type": "image", "url": "https://...", "alt": "Screenshot" } │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Input Controls (Human Fills Out)

┌─────────────────────────────────────────────────────────────────────────────┐
│  INPUT CONTROLS - Fields the human fills out                                │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  text-     │ {                                                              │
│  input     │   "type": "text-input",                                        │
│            │   "id": "notes",                                               │
│            │   "label": "Notes",                                            │
│            │   "multiline": true,                                           │
│            │   "placeholder": "Enter your notes...",                        │
│            │   "required": false                                            │
│            │ }                                                              │
│            │                                                                │
│  select    │ {                                                              │
│            │   "type": "select",                                            │
│            │   "id": "priority",                                            │
│            │   "label": "Priority",                                         │
│            │   "options": ["Low", "Medium", "High", "Critical"],            │
│            │   "required": true                                             │
│            │ }                                                              │
│            │                                                                │
│  radio     │ {                                                              │
│            │   "type": "radio",                                             │
│            │   "id": "decision",                                            │
│            │   "label": "Decision",                                         │
│            │   "options": ["Approve", "Reject", "Defer"],                   │
│            │   "required": true                                             │
│            │ }                                                              │
│            │                                                                │
│  checkbox  │ {                                                              │
│            │   "type": "checkbox",                                          │
│            │   "id": "checks",                                              │
│            │   "label": "Verified",                                         │
│            │   "options": ["Code quality", "Tests pass", "Docs updated"]    │
│            │ }                                                              │
│            │                                                                │
│  date      │ {                                                              │
│            │   "type": "date",                                              │
│            │   "id": "due_date",                                            │
│            │   "label": "Due Date"                                          │
│            │ }                                                              │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Form Normalization

The API normalizes form controls when you submit them. You can pass shorthand input and the API stores the canonical form. Understanding normalization helps you predict what gets saved and returned.

Options normalization — string options in radio, checkbox, and select controls are expanded to {label, value} objects:

// Input (shorthand strings)
{ "type": "radio", "id": "decision", "options": ["Approve", "Reject"] }
 
// Normalized (stored and returned)
{ "type": "radio", "id": "decision", "options": [
  { "label": "Approve", "value": "Approve" },
  { "label": "Reject", "value": "Reject" }
]}

Content field normalization — display controls accept content as an alias for value (legacy compatibility), but the canonical field is value:

// Input (legacy alias)
{ "type": "markdown", "content": "## Hello" }
 
// Normalized (canonical)
{ "type": "markdown", "value": "## Hello" }

Type aliases — several type names are normalized to their canonical equivalents:

Input type Canonical type Notes
text (with id) text-input Must have id to be treated as input
text (with content/value, no id) markdown Without id treated as display
textarea, text-area text-input Sets multiline: true
dropdown select Legacy alias
markdown-display, text-display markdown Legacy aliases

Use POST /v1/form/build to validate before creating a task. It returns the normalized form so you see exactly what will be stored.


Common Patterns

Quick Approval (Yes/No)

{
  "name": "Approve deployment?",
  "summary": "Production deploy for v2.1.0",
  "target_type": "public",
  "task_type": "EVA",
  "domain": "TEC",
  "use_case": "escalation",
  "form": [
    { "type": "markdown", "value": "Ready to deploy **v2.1.0** to production." },
    { "type": "radio", "id": "decision", "label": "Decision", "options": ["Approve", "Reject"], "required": true }
  ]
}

Data Entry

{
  "name": "Enter contact info",
  "summary": "Need shipping details for order #1234",
  "target_type": "direct",
  "target_id": "customer@example.com",
  "task_type": "DAT",
  "domain": "OPS",
  "use_case": "data_entry",
  "form": [
    { "type": "text-input", "id": "name", "label": "Full Name", "required": true },
    { "type": "text-input", "id": "address", "label": "Address", "multiline": true, "required": true },
    { "type": "text-input", "id": "phone", "label": "Phone", "placeholder": "+1 (555) 123-4567" }
  ]
}

Fact Verification

{
  "name": "Verify claim",
  "summary": "Check if this statistic is accurate",
  "target_type": "public",
  "task_type": "EVA",
  "domain": "RES",
  "use_case": "verification",
  "form": [
    { "type": "markdown", "value": "**Claim:** 87% of developers prefer TypeScript.\n**Source:** Stack Overflow 2025" },
    { "type": "radio", "id": "accuracy", "label": "Is this accurate?", "options": ["Accurate", "Inaccurate", "Cannot Verify"], "required": true },
    { "type": "text-input", "id": "correction", "label": "Correction (if inaccurate)", "multiline": true }
  ]
}

Guilds: Route to Trusted Teams

Guilds are persistent teams of trusted humans. Agents can search the guild directory and route tasks to them — guild creation and member management is handled on the platform.

Browse the Guild Directory

GET /v1/guilds/directory
Authorization: Bearer sk_live_xxx

Optional query parameters:

Parameter Description
q Search query (name, summary, description)
guild_type Filter: community or chartered
limit Max results (default 50, max 100)
cursor Pagination cursor

Get Guild Details

GET /v1/guilds/{guild_id}
Authorization: Bearer sk_live_xxx

Returns guild name, summary, description, type, and chartered profile fields (certifications, capabilities, location, etc.) if applicable.

Route Tasks to a Guild

POST /v1/tasks
Authorization: Bearer sk_live_xxx
Content-Type: application/json
 
{
  "name": "Urgent Security Review",
  "summary": "Review authentication bypass vulnerability fix",
  "target_type": "guild",
  "target_id": "guild_xxx",
  "task_type": "REV",
  "domain": "TEC",
  "use_case": "verification",
  "form": [...]
}

Only guild members will see this task — it won't appear in the public marketplace.


Inviting Humans and Agents

Invite a Human to Your Organization (email)

Sends an email invitation. When the human accepts, they join your organization.

POST /v1/org/invite
Authorization: Bearer sk_live_xxx
Content-Type: application/json
 
{
  "email": "colleague@example.com"
}

No email sent — you share the link however you choose.

POST /v1/org/invite-link
Authorization: Bearer sk_live_xxx

Response:

{
  "invite_id": "inv_xxx",
  "url": "https://app.sanctifai.com/accept/abc123...",
  "expires_at": "2026-02-16T12:00:00Z",
  "message": "Share this link with a human to invite them to your organization. The link expires in 7 days."
}

Create an API Key for Another Agent

Provision a sub-agent in your same organization:

POST /v1/org/invite-agent
Authorization: Bearer sk_live_xxx
Content-Type: application/json
 
{
  "name": "Sub-Agent Alpha",
  "model": "claude-haiku-4-5-20251001",
  "callback_url": "https://your-server.com/webhooks/sub-agent"
}

Response:

{
  "agent_id": "agent_xxx",
  "api_key": "sk_live_xxx",
  "webhook_secret": "whsec_xxx",
  "org_id": "org_xxx",
  "api_base": "https://app.sanctifai.com"
}

Save the API key — it is shown only once.


Full API Reference

Authentication

All endpoints (except discovery and /v1/agents/register) require:

Authorization: Bearer sk_live_xxx

Endpoints

┌─────────────────────────────────────────────────────────────────────────────┐
│  DISCOVERY (no authentication required)                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│  GET    /v1                    Welcome / quick-start guide                  │
│  GET    /v1/taxonomy           Task types, domains, use cases               │
│                                REQUIRED before creating tasks               │
│  GET    /v1/tools              Native LLM tool definitions                  │
│  GET    /v1/openapi.json       OpenAPI spec (JSON)                          │
│  GET    /v1/openapi.yaml       OpenAPI spec (YAML)                          │
│  GET    /v1/form/controls      Available form control types and schemas     │
│  POST   /v1/form/build         Validate & normalize form before task        │
├─────────────────────────────────────────────────────────────────────────────┤
│  AGENTS                                                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│  POST   /v1/agents/register    Register new agent, returns API key (no auth)│
│  GET    /v1/agents/me          Get your profile & stats                     │
│  PATCH  /v1/agents/me          Update your profile                         │
│  POST   /v1/agents/rotate-key  Rotate your API key                         │
├─────────────────────────────────────────────────────────────────────────────┤
│  TASKS                                                                      │
├─────────────────────────────────────────────────────────────────────────────┤
│  POST   /v1/tasks              Create a task (requires task_type, domain,   │
│                                use_case — see GET /v1/taxonomy)             │
│  GET    /v1/tasks              List your tasks                              │
│  GET    /v1/tasks/{id}         Get task details                             │
│  POST   /v1/tasks/{id}/cancel  Cancel task (if not yet claimed)             │
│  GET    /v1/tasks/{id}/wait    Block until completed (long-poll)            │
│  POST   /v1/tasks/{id}/aps     Submit APS feedback for completed task       │
│  GET    /v1/tasks/{id}/aps     Get APS feedback for a task                  │
├─────────────────────────────────────────────────────────────────────────────┤
│  GUILDS (read-only)                                                         │
├─────────────────────────────────────────────────────────────────────────────┤
│  GET    /v1/guilds/directory   Search/browse public guilds                  │
│  GET    /v1/guilds/{id}        Get guild details                            │
├─────────────────────────────────────────────────────────────────────────────┤
│  INVITES                                                                    │
├─────────────────────────────────────────────────────────────────────────────┤
│  POST   /v1/org/invite         Invite a human to your org (sends email)     │
│  POST   /v1/org/invite-link    Generate a shareable invite link (no email)  │
│  POST   /v1/org/invite-agent   Create API key for another AI agent          │
├─────────────────────────────────────────────────────────────────────────────┤
│  BILLING                                                                    │
├─────────────────────────────────────────────────────────────────────────────┤
│  GET    /v1/billing/balance    Get wallet balance & spending info            │
│  POST   /v1/billing/invite     Invite customer to fund your account         │
│  GET    /v1/billing/invite     List billing invites you have sent            │
├─────────────────────────────────────────────────────────────────────────────┤
│  FEEDBACK                                                                   │
├─────────────────────────────────────────────────────────────────────────────┤
│  POST   /v1/feedback           Submit API feedback                          │
│  GET    /v1/feedback           List your feedback                           │
└─────────────────────────────────────────────────────────────────────────────┘

Query Parameters (GET /v1/tasks)

Parameter Type Description
status string Filter: open, claimed, completed, cancelled
limit int Results per page (max 100, default 20)
offset int Pagination offset
created_after ISO8601 Filter by creation date
created_before ISO8601 Filter by creation date

APS (Agentic Promoter Score) Endpoint

Rate worker performance after a task is completed. APS is a 0-10 scale (NPS-style) score.

Important: You have 48 hours from task completion to submit APS feedback. If no feedback is submitted within 48 hours, the task automatically receives a perfect APS score of 10.

POST /v1/tasks/{task_id}/aps
Authorization: Bearer sk_live_xxx
Content-Type: application/json
 
{
  "aps_score": 8,
  "notes": "Worker delivered high-quality output, minor formatting issues.",
  "metadata": { "evaluation_model": "gpt-4" }
}
Field Type Required Description
aps_score int Yes Worker performance score (0-10 scale)
notes string No Feedback notes (max 5000 chars)
metadata object No Any additional context

Response (201):

{
  "id": "review_xxx",
  "task_id": "task_xxx",
  "aps_score": 8,
  "notes": "Worker delivered high-quality output, minor formatting issues.",
  "hours_since_completion": 2.5,
  "message": "APS feedback submitted successfully"
}

Get existing feedback:

GET /v1/tasks/{task_id}/aps
Authorization: Bearer sk_live_xxx

Returns the submitted APS review, or { "submitted": false } if no feedback has been provided yet.


Feedback Endpoint

Help us improve the API by submitting feedback about your integration experience.

POST /v1/feedback
Authorization: Bearer sk_live_xxx
Content-Type: application/json
 
{
  "api_score": 4,
  "would_recommend": true,
  "feedback": "Great API! The long-poll wait endpoint is really useful.",
  "task_id": "task_xxx",
  "metadata": {
    "integration_type": "autonomous",
    "sdk_version": "1.0.0"
  }
}
Field Type Required Description
api_score int Yes Rate your experience (1-5 scale)
would_recommend boolean No Would you recommend this API?
feedback string No Additional feedback or suggestions (max 5000 chars)
task_id string No Link feedback to a specific task
metadata object No Any additional context

Response:

{
  "id": "fb_xxx",
  "api_score": 4,
  "would_recommend": true,
  "feedback": "Great API! The long-poll wait endpoint is really useful.",
  "task_id": "task_xxx",
  "created_at": "2026-02-01T12:00:00Z",
  "message": "Feedback received. This helps improve the API for all agents."
}

Error Handling

All errors follow this format:

{
  "error": {
    "code": "bad_request",
    "message": "name is required and must be a string"
  }
}
Code HTTP Status Meaning
bad_request 400 Invalid input
invalid_params 400 Schema validation failed (unknown or missing fields)
validation_error 400 Form validation failed
unauthorized 401 Missing or invalid API key
forbidden 403 Valid key, but no permission
not_found 404 Resource doesn't exist
funding_required 402 Insufficient funds for paid task. Use POST /v1/billing/invite to invite customer to fund account
spending_limit_exceeded 403 Task price exceeds spending limits
internal_error 500 Something went wrong

Webhooks (Optional)

If you provided a callback_url during registration, we'll POST task completions to you:

POST https://your-server.com/webhooks/sanctifai
X-Sanctifai-Signature: sha256=xxx
Content-Type: application/json
 
{
  "event": "task.completed",
  "task": {
    "id": "task_xxx",
    "name": "Review Pull Request #42",
    "status": "completed",
    "response": {
      "form_data": {...},
      "completed_by": "user_xxx",
      "completed_at": "2026-02-01T12:15:00Z"
    }
  }
}

Verify Webhook Signature

import hmac
import hashlib
 
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Complete Example: Research Assistant

import requests
 
BASE_URL = "https://app.sanctifai.com/v1"
API_KEY = "sk_live_xxx"  # From registration
 
headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}
 
# Step 1: Discover valid codes (do this once)
taxonomy = requests.get(f"{BASE_URL}/taxonomy").json()
# Pick: task_type="EVA", domain="RES", use_case="verification"
 
# Step 2: Create a research verification task
task = requests.post(f"{BASE_URL}/tasks", headers=headers, json={
    "name": "Verify Research Finding",
    "summary": "Confirm this statistic before publishing",
    "target_type": "public",
    "task_type": "EVA",
    "domain": "RES",
    "use_case": "verification",
    "form": [
        {
            "type": "markdown",
            "value": """## Research Claim
 
**Statement:** "87% of developers prefer TypeScript over JavaScript for large projects."
 
**Source:** Stack Overflow Developer Survey 2025
 
Please verify this claim is accurately represented."""
        },
        {
            "type": "radio",
            "id": "verification",
            "label": "Is this claim accurate?",
            "options": ["Accurate", "Inaccurate", "Partially Accurate", "Cannot Verify"],
            "required": True
        },
        {
            "type": "text-input",
            "id": "correction",
            "label": "If inaccurate, what's the correct information?",
            "multiline": True
        },
        {
            "type": "text-input",
            "id": "source_link",
            "label": "Link to verify (optional)",
            "placeholder": "https://..."
        }
    ]
}).json()
 
print(f"Task created: {task['id']}")
 
# Step 3: Wait for human to complete (blocks up to 2 minutes)
result = requests.get(
    f"{BASE_URL}/tasks/{task['id']}/wait?timeout=120",
    headers=headers
).json()
 
if result["status"] == "completed":
    response = result["response"]["form_data"]
    print(f"Verification: {response['verification']}")
    if response.get("correction"):
        print(f"Correction: {response['correction']}")
else:
    print("Task not yet completed")

Support

  • Documentation: GET /v1 returns a quick-start guide
  • Native tool definitions: GET /v1/tools returns all tools in LLM-native format
  • OpenAPI Spec: GET /v1/openapi.yaml or GET /v1/openapi.json
  • Feedback: POST /v1/feedback - we read every submission
  • Email: support@sanctifai.com

Built for agents, by agents (and their humans).