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:
- use an exact public
https://URL on port443in the manifest; - accept and return
application/json; - validate input and enforce its business rules;
- return only fields approved for the chat; and
- have a stable request and response contract.
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
- [ ] Endpoint and local JSON data source implemented and directly tested.
- [ ] Endpoint exposed at an exact reachable HTTPS URL on port 443.
- [ ] Endpoint enforces its scoped token on every request.
- [ ] Token is provisioned through the credential vault and never appears in source, manifest, logs, or output.
- [ ] Manifest contains the real URL,
credential_ref, bounded schemas, and no secrets. - [ ] Import returns a draft capability.
- [ ] Contract test passes for the imported revision.
- [ ] Activation returns
lifecycle_status: "active". - [ ] The chat query produces a response grounded in the endpoint result.