Source: docs/integration/capability-handoff.md

Self-service business capabilities

Use this document when your application's chat needs live data or actions from

your application. An integrator can register and activate capabilities through

the Public API with its API key.

Lifecycle

Implement endpoint + scoped token → test endpoint → expose HTTPS URL → encrypt token in vault
→ import draft → contract test → activate → verify chat response

The API key determines which capabilities the request can manage. No account or

organization identifier is required in the manifest or URL.

Endpoint requirements

Your endpoint must:

A local JSON-backed endpoint is valid for development, but localhost cannot

be called by deployed Workers. Test it locally first, then use an authorized

HTTPS tunnel for a development test or deploy it through the application's

authorized deployment path. Do not use an example or placeholder URL.

For person lookup, prefer one read-only endpoint:

POST https://api.example.com/v1/people/lookup
Content-Type: application/json

{"name":"Juan Pérez"}
{
  "person_id": "person_123",
  "name": "Juan Pérez",
  "email": "juan@example.com"
}

Create <project-root>/forgium-agent.capabilities.json

Use the real endpoint URL. Every endpoint must be protected with a scoped

token. Register the token in the vault first; never put a token or private

header in the manifest.

{
  "domain": "people",
  "capabilities": [
    {
      "name": "lookup_person",
      "description": "Looks up a person's approved contact details by name or DNI.",
      "when_to_use": "Use when the user asks for a person's email, contact details, or data by DNI.",
      "method": "POST",
      "url": "https://your-reachable-host.example/v1/people/lookup",
      "auth": { "type": "bearer", "credential_ref": "cred_people_api" },
      "safe_headers": {
        "accept": "application/json",
        "content-type": "application/json"
      },
      "input_schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": { "type": "string", "maxLength": 200 },
          "dni": { "type": "string", "maxLength": 20 }
        },
        "anyOf": [{ "required": ["name"] }, { "required": ["dni"] }]
      },
      "output_schema": {
        "type": "object",
        "additionalProperties": false,
        "required": ["person_id", "name"],
        "properties": {
          "person_id": { "type": "string", "maxLength": 100 },
          "name": { "type": "string", "maxLength": 200 },
          "email": { "type": "string", "maxLength": 254 },
          "phone": { "type": "string", "maxLength": 50 }
        }
      },
      "business_rules": ["The backend is authoritative for person data."],
      "examples": [],
      "side_effect": "read",
      "requires_approval": false,
      "timeout_ms": 5000,
      "retry_count": 1
    }
  ]
}

Every model-visible string needs maxLength; arrays need maxItems; objects

need additionalProperties: false.

Register, test, and activate

export CAPABILITY_ID=""

# The API implementation creates PEOPLE_API_TOKEN and validates it on every lookup.
# This request stores only AES-GCM ciphertext in the credential vault.
curl -fsS -X PUT "$FORGIUM_AGENT_BASE_URL/v1/capability-credentials/cred_people_api" \
  -H "Authorization: Bearer $FORGIUM_AGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(printf '{\"auth_type\":\"bearer\",\"secret\":%s}' "$(jq -Rn --arg value "$PEOPLE_API_TOKEN" '$value')")"

IMPORT_RESPONSE="$(curl -fsS -X POST "$FORGIUM_AGENT_BASE_URL/v1/capabilities/import" \
  -H "Authorization: Bearer $FORGIUM_AGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: capability-import-$(date +%s)" \
  --data-binary @forgium-agent.capabilities.json)"

# Read the returned capabilities[0].id into CAPABILITY_ID with your JSON tool.
curl -fsS -X POST "$FORGIUM_AGENT_BASE_URL/v1/capabilities/$CAPABILITY_ID/test" \
  -H "Authorization: Bearer $FORGIUM_AGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"arguments":{"name":"Juan Pérez"}}'

curl -fsS -X POST "$FORGIUM_AGENT_BASE_URL/v1/capabilities/$CAPABILITY_ID/activate" \
  -H "Authorization: Bearer $FORGIUM_AGENT_API_KEY"

Useful routes:

| Operation | Route |
|---|---|
| List own capabilities | `GET /v1/capabilities` |
| Import draft | `POST /v1/capabilities/import` |
| Get or revise one | `GET` / `PATCH /v1/capabilities/{capabilityId}` |
| Contract test | `POST /v1/capabilities/{capabilityId}/test` |
| Activate or disable | `POST /v1/capabilities/{capabilityId}/activate` or `/disable` |

Credentials

Provision the endpoint token through PUT /v1/capability-credentials/{credentialRef}

before importing its manifest. The secret is AES-GCM ciphertext at rest; it is

never returned by the API.

| `auth.type` | Required request fields |
|---|---|
| `bearer` | `auth_type: "bearer"`, `secret` |
| `api_key` | `auth_type: "api_key"`, `header_name: "x-api-key"` or `"x-api-token"`, `secret` |
| `basic` | `auth_type: "basic"`, `secret` containing the pre-encoded Basic value |

auth.type: "none" is rejected for self-service imports. Do not leave a

business endpoint open just to make a capability work.

Definition of done