# Running a skill

A skill is the unit you run. The hosted skill agent selects the underlying tools, runs them, enforces your budget, and bills you. One account, one token, one endpoint, every capability.

## Endpoint

```
POST https://skill.askfaro.com/skills/{skill}/run
Authorization: Bearer faro_<your_key>
Content-Type: application/json

{
  "intent": { "prompt": "a red bicycle" },   // a prompt string wrapped, or a structured object
  "max_credits": 200,                          // optional hard cap; the run aborts rather than exceed it
  "confirm_above": 50,                         // optional soft ceiling; returns a quote instead of spending
  "continuation": "f1..."                      // optional; a signed token from a prior quote, to resume that plan
}
```

Discover skill ids with `GET https://api.askfaro.com/tools/search?q={query}` (the leaves are skills) or the [catalog map](https://askfaro.com/llms/catalog.md). Inspect one with `GET https://skill.askfaro.com/skills/{skill}`.

## Auth

Three token types are accepted:

| Prefix | Use case | Extra headers |
|---|---|---|
| `faro_` | Personal / agent API key | (none) |
| `<JWT>` | Logged-in session | (none) |
| `faro_partner_` | Partner platform running for an end user | `X-Faro-User: <external_id>` |

Fastest key: `POST https://api.askfaro.com/auth/agent-signup` with `{"email": "..."}` returns a `faro_` key in one call (no password). Or create one from a session: `POST https://api.askfaro.com/tokens/` (requires a JWT from `POST https://api.askfaro.com/auth/login`).

## Verification & access tiers

Skills are tiered by who may run them:

- **unverified**: self-hosted skills (no external provider). Runnable by any account, including a fresh unverified one.
- **verified**: free proxied external data. Requires a verified email.
- **paid**: priced skills. Requires a verified email and credits.

A new account starts unverified and may only run unverified skills; anything else fails with `error.code: auth`. Verify via the signup email or `POST https://api.askfaro.com/auth/resend-verification` (a completed purchase also counts). Partner traffic is funded and always treated as verified.

## Response

The run always returns HTTP 200 with the canonical `SkillResult` envelope; `status` carries the outcome, so you integrate one handler:

```json
{
  "faro_envelope": "1",
  "id": "…",                         // run id
  "skill": "image",
  "status": "success",                // "success" | "failed" | "needs_input"
  "result": {                         // present when status is "success"
    "kind": "information",            // "information" | "file" | "files"
    "summary": "…",
    "data": { ... },                  // structured result (object) for kind "information"
    "sources": [{ "url": "…" }]       // optional provenance (citations / source links)
    // kind "file": "file": { "download_url": "…", "mime": "…", "size_bytes": 0, "filename": "…" }
  },
  "error": {                          // present when status is "failed"
    "code": "upstream_failed",        // invalid_input | auth | rate_limited | insufficient_credits | not_found | timeout | upstream_failed | internal
    "message": "…",
    "retryable": true
  },
  "needs_input": {                    // present when status is "needs_input"
    "question": "…",
    "missing": ["…"]
  },
  "continuation": "f1…",              // signed token; echo it back to resume a quote or paginate
  "meta": {
    "credits_charged": 50,            // what this run cost you
    "credits_estimated": 50,
    "started_at": "…",
    "completed_at": "…"
  }
}
```

`result.data` is always a JSON object. Faro's vendor cost and margin are never returned; `meta.credits_charged` is your price.

## Status & errors

The HTTP status is 200 for every run except auth; read `status` and, on failure, `error.code`:

| Where | Value | Meaning |
|---|---|---|
| HTTP | 401 | Missing or invalid token |
| `status` | `needs_input` | A clarification, or a budget quote (crossed `confirm_above`). Re-run with the `continuation`. |
| `error.code` | `auth` | Email not verified, or this skill needs a verified account. Verify, or call `POST https://api.askfaro.com/auth/resend-verification` |
| `error.code` | `insufficient_credits` | Out of credits, call `POST /credits/purchase` |
| `error.code` | `invalid_input` | The intent is missing a required field, or would exceed `max_credits` |
| `error.code` | `not_found` | No such skill |
| `error.code` | `upstream_failed` | The underlying tool failed; auto-refunded when priced `charge_on: success` |

## Budget ceilings

- `max_credits`: a hard cap. A run whose worst-case cost would exceed it aborts with `invalid_input` rather than spending.
- `confirm_above`: a soft ceiling. A run that would cross it stops at `status: needs_input` and returns a quote carrying a signed `continuation` token. Re-run with that token (and a high enough `max_credits`) to execute the pinned plan, so you never double-route or double-charge.

## Pricing modes

- `fixed_per_request`: the skill charges a fixed credit cost per run.
- `tool_returned`: the skill reports its cost in the response (e.g. token-based pricing for LLM skills).

Free tier: self-hosted skills and skills flagged `free_tier` cost 0 credits within a monthly allowance (larger once verified), and run even at a $0 balance. Beyond the allowance the normal price applies. Otherwise 1,000 credits = $1.00 USD, and the price shown is what you pay end-to-end; Faro's margin is bundled in.

## See also

- [CLI](https://askfaro.com/llms/cli.md)
- OpenAPI spec: https://api.askfaro.com/openapi.json
- Structured guide (JSON): https://api.askfaro.com/docs/
