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":"[email protected]"}' --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. inline HTTP column
Two different things share the "Custom HTTP" name. Pick by whether the request is reusable:
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. Inline 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. It runs in the background on Oxygen like any other tool column — web, CLI, and MCP all queue the same durable run — and {{column_key}} tokens in the url, headers, and body are substituted from each row.
Where an inline column's secrets come from
A secrets entry is a reference, never a value: {"acme": {"env": "ACME_KEY"}}, used in the request as {{secrets.acme}}. Where ACME_KEY is read depends on where the column runs, and the two are deliberately different:
| Run | {"env": "ACME_KEY"} resolves from |
|---|---|
| Background (web, CLI, MCP) | this workspace's registered custom integration with slug acme_key, reading its encrypted api_key secret |
oxygen columns run <table> <column> --local | the environment variable ACME_KEY on your own machine |
Oxygen never reads the server's own environment for a workspace column, so a background run whose reference names no registered integration fails that row with custom_http_secret_unavailable rather than silently sending a blank credential. Register it once and every surface can run the column:
oxygen custom-integrations apply --slug acme_key --name "Acme" --base-url https://api.acme.com --auth bearer
oxygen custom-integrations connect acme_key --api-key "$ACME_KEY"Use --local when the key must not leave your machine.
Effect, approval, and cost for an inline column
A GET is a read: it needs no --approved and no --max-credits. Any other method is an external write and requires --approved like every other side-effecting tool column. Either way Oxygen charges 0 credits — you own the upstream API cost — and --dry-run previews the templated request for one real row with every secret masked as ***, naming which references this workspace can resolve and which it cannot.
oxygen columns run leads lead_score --limit 1 --dry-run --json
oxygen columns run leads lead_score --limit 25 --background --jsonRelated
- Integrations — connection state and auth modes.
- Recipes — calling custom tools from workflow code.
- Approvals — write-effect operations and live runs.
Mailbox warmup and monitoring compatibility
Which mailbox origins OXYGEN can warm up and monitor, how to import each one, and what to do when a pairing is blocked.
Enrichment catalogue
Every data type a table can fill, its status, inputs, fields, region coverage and the provider chain behind it — generated from the routing registry.