Custom HTTP integrations
Turn any HTTP API into catalog tools with a JSON manifest.
A custom HTTP integration registers your own HTTP API as provider tools. Each operation you declare becomes a callable tool (custom_http.<slug>.<operation>) usable from tool columns, workflows, and recipes, with the credential injected server-side.
Use it when the recipe-native HTTP tools are not enough: oxygen.http_json_request (GET) and oxygen.http_json_post (POST with JSON body) only reach anonymous public HTTPS endpoints — no credentials, no custom headers. Custom HTTP operations support GET, POST, PUT, PATCH, and DELETE with typed body/query/path fields and stored credentials.
Registering
oxygen custom-integrations apply --manifest ./my-integration.json --json
oxygen custom-integrations list --jsonapply requires exactly one of --manifest <path> (a JSON file) or --manifest-json '<json>' (inline). Credentials are not passed here — see Credentials.
Manifest shape
{
"version": 1,
"slug": "lead_scorer",
"name": "Lead Scorer",
"description": "Score leads via our internal API.",
"baseUrl": "https://api.example.com/",
"auth": { "type": "bearer" },
"operations": [
{
"operation": "score_lead",
"displayName": "Score Lead",
"method": "POST",
"path": "/score",
"summary": "Score a lead by email.",
"effect": "read",
"fields": [
{ "key": "email", "type": "string", "required": true, "description": "Lead email.", "location": "body" }
]
}
]
}This mints the tool custom_http.lead_scorer.score_lead.
Top-level fields
| Field | Required | Notes |
|---|---|---|
version | No | Defaults to 1; must be 1 if set. |
slug | Yes | Integration id. Must match ^[a-z][a-z0-9_]{0,62}$. |
name | Yes | Display name. |
description | No | Defaults to a generated description. |
baseUrl | Yes | Public HTTPS URL. Private/internal hosts are rejected. A base path and query are preserved when the operation path is appended. |
auth | No | Defaults to { "type": "none" }. See Auth. |
operations | Yes | Non-empty array. Operation ids must be unique. |
Operation fields
| Field | Required | Notes |
|---|---|---|
operation | Yes | Operation id, same slug rules as above. |
method | Yes | GET, POST, PUT, PATCH, or DELETE. |
path | Yes | Must start with /. Appended to baseUrl; supports {param} segments filled by location: "path" fields. |
displayName | No | Defaults to a titleized operation id. |
summary | No | Defaults to <METHOD> <path>. |
effect | No | read (default) or write. Writes follow approval rules. |
sideEffectClass | No | Derived from effect unless set. |
idempotent | No | Defaults to true for GET and false otherwise. Set true only when replaying the same request is safe; this lets a reclaimed worker retry after an ambiguous interruption. |
fields | No | Typed inputs — see below. With no fields, a GET forwards the raw input as query params and other methods forward it as the JSON body. |
timeoutMs | No | Default 10000, max 300000 (5 minutes). Values outside the supported range are rejected rather than silently clamped. |
maxResponseBytes | No | Default 1000000, max 5000000. |
outputPath | No | Dot-path to project out of the response. |
inputSchema / outputSchema | No | Optional JSON Schemas for the tool descriptor. |
Field entries
| Field | Required | Notes |
|---|---|---|
key | Yes | Input name (slug rules). |
type | No | string (default), number, boolean, string[], number[], object, object[], array. integer is accepted as an alias for number. |
required | No | Defaults to true. |
description | No | Defaults to the key. |
location | No | body, query, or path. Defaults to query on GET, otherwise body. |
wireKey | No | Rename the key on the wire. |
Auth
auth.type | Extra fields | Sent as |
|---|---|---|
none | — | — |
bearer | — | Authorization: Bearer <secret> |
api_key_header | header, prefix? | <header>: <prefix><secret> |
api_key_query | param | ?<param>=<secret> |
basic | username | Authorization: Basic base64(<username>:<secret>) |
Compatibility aliases are accepted for imported manifests: api_key for api_key_header, headerName for header, and slug, id, or key for an operation id. New manifests should use the canonical names above.
Credentials
The manifest never contains the secret (apply rejects a secrets field). After apply, attach the token/key from the terminal:
oxygen custom-integrations connect lead_scorer --secrets-file ./.env--secrets-file reads a .env-style file (a single entry, or one named API_KEY/TOKEN/PASSWORD/SECRET) so the credential stays out of shell history. You can also attach the credential from the web Connections page (/connections/custom_http). Either way, Oxygen stores the credential encrypted and injects it server-side at call time — recipe and column code never sees it, and it never enters a recipe bundle or run log.
Calling the tools
oxygen tools get custom_http.lead_scorer.score_lead --json
oxygen tools run custom_http.lead_scorer.score_lead --input-json '{"email":"a@b.com"}' --mode dry-run --jsonCustom HTTP tools use the no_bill credit posture: Oxygen charges 0 credits and the connected organization owns any upstream API cost. A live write operation still requires explicit approval, but no max_credits value; read-only operations do not require approval. Run summaries echo idempotent, timeout_ms, max_response_bytes, and output_path so the execution contract remains inspectable.
In a recipe, allowlist the tool id and call it with ctx.tools.run("custom_http.lead_scorer.score_lead", { email }, { key }). In a table, bind it as a tool column.
Custom integration vs. local HTTP column
Two different things share the "Custom HTTP" name. Pick by where the credential must live:
A. Reusable custom integration (this page). oxygen custom-integrations apply registers org-wide custom_http.<slug>.<operation> tools; the credential is stored encrypted in Oxygen, and the tools run in the background from tool columns, workflows, and recipes with billing and provenance.
B. Local HTTP column (the web app's "Add column → HTTP API"). A single tool column whose definition uses mode: "custom_http" with an inline request block; its secrets name local environment variables, so it executes only in your own CLI process via oxygen columns run <table> <column> --local — never server-side, never in the background.
Use B only when the key must not leave your machine.
Related
- Integrations — connection state and auth modes.
- Recipes — calling custom tools from workflow code.
- Approvals — write-effect operations and live runs.