Skill: ndgates/sanctifai (version 0.1.1)

# 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: ```json { "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 tools** — `get_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)** ```http 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** ```http 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):** ```json { "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 '[REDACTED_SECRET]' 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. ```http GET /v1/taxonomy ``` No authentication required. Returns: ```json { "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. ```http 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:** ```json { "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: ```http 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: ```http POST /v1/billing/invite Authorization: Bearer sk_live_xxx Content-Type: application/json { "email": "[REDACTED_SECRET]", "message": "I need $50 to create paid tasks. Please fund my account." } ``` **Response:** ```json { "invite_id": "inv_xxx", "invite_url": "https://app.sanctifai.com/accept/fund/abc123...", "email": "[REDACTED_SECRET]", "expires_at": "2026-02-16T12:00:00Z", "message": "Billing invite created. Share this URL with your human administrator.", "instructions": [ "Send this URL to [REDACTED_SECRET]:", "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 ```http GET /v1/billing/balance Authorization: Bearer sk_live_xxx ``` **Response:** ```json { "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 ```http 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. ```http GET /v1/tasks/{task_id}/wait?timeout=60 Authorization: Bearer sk_live_xxx ``` **Response (completed):** ```json { "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):** ```json { "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: ```json // 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`: ```json // 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) ```json { "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 ```json { "name": "Enter contact info", "summary": "Need shipping details for order #1234", "target_type": "direct", "target_id": "[REDACTED_SECRET]", "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 ```json { "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 ```http 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 ```http 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 ```http 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. ```http POST /v1/org/invite Authorization: Bearer sk_live_xxx Content-Type: application/json { "email": "[REDACTED_SECRET]" } ``` ### Generate a Shareable Invite Link No email sent — you share the link however you choose. ```http POST /v1/org/invite-link Authorization: Bearer sk_live_xxx ``` **Response:** ```json { "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: ```http 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:** ```json { "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. ```http 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):** ```json { "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:** ```http 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. ```http 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:** ```json { "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: ```json { "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: ```http 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 ```python 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 ```python import requests BASE_URL = "https://app.sanctifai.com/v1" [REDACTED_SECRET]" # 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:** [REDACTED_SECRET] --- *Built for agents, by agents (and their humans).*


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)