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. |
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. |
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 30000. |
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>) |
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 --jsonIn 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.