API REFERENCE

The credential vault

A per-user vault for secrets. Store a token once, reference it in tool auth by name, and the raw value never reaches the model.

Every credential endpoint requires signed identity mode. The user_context must be signed, provisioning is server to server. There is no browser path: the widget never calls these routes, and an end user cannot add a credential from the chat UI.
Unlike the knowledge, scheduled-task and MCP OAuth routes, all three credential endpoints skip the project origin allowlist. A backend call with no Origin header is served normally even when the project has an allowlist configured. Signed identity is the only control on these routes.
FieldTypeDescription
SIGNED_IDENTITY_REQUIRED403The project itself is not in signed identity mode. Switch the project over; no request body will fix this.
IDENTITY_NOT_CONFIGURED401The project is in signed mode but has no signing secret yet.
IDENTITY_SIGNATURE_REQUIRED401The user_context carried no _sig or no _ts.
IDENTITY_SIGNATURE_INVALID401The HMAC did not verify, or _ts was not an integer. Usually a canonical-JSON mismatch in your signer.
IDENTITY_SIGNATURE_EXPIRED401_ts is more than 300 seconds from server time, in either direction. Sign at request time, not at session start.
IDENTITY_ID_REQUIRED401The signature verified but the context has no id.
USER_ID_REQUIRED400The id is missing or the literal string "anonymous".

Store a credential

PUT/v1/credentials

Stores a secret for one user, encrypted at rest with Fernet. PUT is upsert: reusing a name replaces the value. The raw secret is write-only, once stored you can only read a masked hint.

FieldTypeDescription
user_contextrequiredobjectThe signed end-user identity the secret belongs to.
namerequiredstringReference name. Up to 64 chars, [A-Za-z0-9_.-] only.
valuerequiredstringThe secret. Up to 8 KB.
FieldTypeDescription
INVALID_NAME400The name is empty, longer than 64 characters, or contains something other than letters, digits, _ . or -.
VALUE_REQUIRED400The value was empty. There is no way to store a blank secret; delete it instead.
VALUE_TOO_LARGE413The value exceeds 8192 bytes of UTF-8.
CRED_CAP_REACHED409This user already holds 50 credentials and the name is a new one. Overwriting an existing name is still allowed at the cap.
RATE_LIMIT_EXCEEDED429More than 30 writes in a minute or 300 in a day for this user on this project. The response carries a Retry-After header. GET and DELETE are not throttled.
bash
curl -X PUT https://agentifys.ai/v1/credentials \
  -H "Authorization: Bearer era_your_project_key" \
  -H "Content-Type: application/json" \
  -d '{
    "user_context": { "id": "u_42", "_ts": 1735689600, "_sig": "..." },
    "name": "github_token",
    "value": "ghp_xxxxxxxxxxxxxxxx"
  }'

Response, the name plus a masked hint (never the value). The hint is the first eight characters, an ellipsis, and the last four — a value of eight characters or fewer masks to **** instead.

json
{ "name": "github_token", "secret_hint": "ghp_ABCD...wxyz" }

List and revoke

GET/v1/credentials

Lists the calling user's credentials as masked hints. Pass the signed identity as a user_context query param (URL-encoded JSON).

bash
# List one user's masked credentials. Identity in the user_context query param on a GET.
curl "https://agentifys.ai/v1/credentials?user_context=$(python -c 'import json,urllib.parse; print(urllib.parse.quote(json.dumps({"id":"u_42","_ts":1735689600,"_sig":"..."})))')" \
  -H "Authorization: Bearer era_your_project_key"

Response, each credential as its name, masked hint, active flag and timestamps, ordered by name:

json
{
  "credentials": [
    {
      "name": "github_token",
      "secret_hint": "ghp_ABCD...wxyz",
      "is_active": true,
      "created_at": "2026-08-14 09:12:44+00:00",
      "updated_at": "2026-09-01 17:03:10+00:00"
    }
  ]
}
Both of these put the entire signed user_context in the request line, so it is written to every access log between you and Agentifys. Sign the smallest identity that works — an id and nothing else — for these two calls. If your standard identity payload includes an auth_token, a live bearer token ends up in those logs.
DELETE/v1/credentials/{name}

Revokes one credential by name. The name is required (there is no clear-all route), and the signed identity is passed as a user_context query param.

bash
curl -X DELETE "https://agentifys.ai/v1/credentials/github_token?user_context=%7B%22id%22%3A%22u_42%22%2C%22_ts%22%3A1735689600%2C%22_sig%22%3A%22...%22%7D" \
  -H "Authorization: Bearer era_your_project_key"
json
{ "success": true }

Use it in a tool

A webhook tool has no auth object. Reference a stored secret from the tool's webhook_headers (or webhook_params) with {{user.creds.<name>}}. Those two dictionaries are the only place a placeholder resolves — webhook_url is used literally. At call time Agentifys decrypts the value and substitutes it into the outgoing request. The model sees neither.

A tool that authenticates with the stored GitHub token:

json
{
  "executor_type": "webhook",
  "webhook_url": "https://api.github.com/user/repos",
  "webhook_method": "GET",
  "webhook_headers": {
    "Authorization": "Bearer {{user.creds.github_token}}"
  }
}

For a remote MCP server, the placeholder goes in the server's token field on its own, with no scheme prefix.

A placeholder that does not resolve is replaced with an empty string and the webhook request is sent anyway, so a typo'd credential name produces Authorization: Bearer with nothing after it and a 401 from your API. MCP auth behaves the other way and refuses the call with TOOL_DISABLED. Neither surfaces as “credential missing” on its own — check the resolved value at your endpoint.
To forward a live end-user JWT instead of a stored secret, use {{user.auth_token}}. It carries the token from the current request rather than the vault, which also means it is empty in a scheduled run: the scheduler rebuilds the user from a snapshot of the id, name, email, role and company, with no token. Vault credentials still resolve there.

Provisioning secrets from your backend is the common pattern. See the per-user credentials guide for the end-to-end flow.