Skip to content

Guides

Connect a Custom API

Store a credential for any header-authenticated API — an internal service, a niche vendor, a self-hosted deployment — with the Custom managed-secret template.

Use a catalog template whenever one exists — it ships a verified injection contract, save-time credential verification, and (for many providers) an operation catalog that content rules can bind to. For everything else — an internal platform service, a regional SaaS vendor, a self-hosted deployment — the Custom template stores the credential with the same vault storage, grants, rotation, audit trail, and policy enforcement; the operator supplies the injection contract the catalog would otherwise provide. A custom provider can also carry its own logo, a path its keys are checked against when they are saved, and the API operations its policy rules name — pulled from the API’s OpenAPI document or added by hand.

This also covers an integration where each end user brings their own key (a lender’s integration key, for example): the end user enters it on the hosted Connect page, under the end-user key setup the operator configures, and it is checked against the same path.

Credential typeInjected as
bearer_tokenAuthorization: Bearer <token> (or any format containing {token})
header_keyA configured header, e.g. X-API-Key: <key>
basic_authAuthorization: Basic <token> — store the base64-encoded username:password pair as the credential value
aws_sig_v4AWS Signature V4 signing — intended for AWS-compatible APIs; see the AWS reference

Three settings define how the credential reaches the API:

  • Injection header — the header name the credential is written into (default Authorization). Security-critical and hop-by-hop headers (Host, Cookie, Content-Type, X-Forwarded-For, …) are rejected.
  • Injection format — the header value template. It must contain the {token} placeholder, which is replaced with the stored credential: Bearer {token}, Token {token}, or just {token}.
  • Additional injection rules — up to 10 extra injections for APIs that need more than one value per request, or that authenticate via query parameter. Each rule targets a header or a query_param, names its key, and takes its value from token (the primary credential) or additional_credentials.<field> (a named secondary credential, stored in the vault alongside the primary value and equally write-only):
[
{"target": "header", "key": "X-App-Key", "value_source": "additional_credentials.app_key"},
{"target": "query_param", "key": "api_key", "value_source": "token"}
]

Rotating the primary credential keeps each additional_credentials.<field> an injection rule reads, unless the rotation sends a new value for it. An aws_sig_v4 credential keeps a stored region and service the same way.

Dashboard

Open the app, go to Managed Secrets → Add Provider, and pick the Custom card. Name the provider after the API (the name generates the slug), choose the Credential Type, set the Injection Header and Injection Format, add any Additional Injection Rules, paste the credential and a value for each field a rule reads (a rule sends nothing while its field has no value; the dashboard and the CLI warn and still save, so the field can be set later with a fields-only update), click Next, list the Allowed API hosts on the Access Policy step, optionally upload a Logo, set a Credential check path and an OpenAPI document URL (see steps 3 and 5), and click Create Provider.

With the AWS SigV4 credential type, Alter signs each request and sets the header itself, so there is no injection header or format to set: paste the Access Key ID, fill Secret Access Key, and add a Session Token, Region or Service only when the API needs them (see the AWS reference). For AWS itself, the AWS template is the usual choice.

CLI

Omit --template to create a custom provider. This example stores an API key for an internal billing service that authenticates with X-Api-Key and requires a workspace id on every call:

Terminal window
printf '%s' "$BILLING_API_KEY" | alter managed-secrets create \
--name "Internal Billing API" \
--credential-type header_key \
--injection-header X-Api-Key \
--injection-format '{token}' \
--injection-rule @./injection-rules.json \
--credential-field workspace_id=ws_12345 \
--credential-value -

where injection-rules.json contains:

[{"target": "header", "key": "X-Workspace-Id", "value_source": "additional_credentials.workspace_id"}]

The host allowlist bounds where the credential can be sent — exact hosts (billing.internal.example.com) or leading-wildcard subdomains (*.example.com).

The dashboard’s create form includes the Allowed Hosts field. The CLI sets it as a follow-up step (or in one call via an --input JSON body carrying allowed_hosts):

Terminal window
alter managed-secrets set-allowed-hosts <provider> --host billing.internal.example.com

Name a credential check path: a path on the API that answers 401 to a bad key, such as /v1/me. Whenever a key is saved (created, rotated, or entered by an end user on the hosted Connect page), Alter sends a GET with the key, injected the provider’s way, to that path on the first exact allowed host. A 401 refuses the save; any other answer saves the key, and a response that does not prove the key was checked (an endpoint that also answers without a key, a network failure) is reported as inconclusive. With no path, a provider whose key travels in several headers, or allowed hosts that are all wildcards, the check is reported as skipped with the reason, and the key is verified on first use.

Terminal window
alter managed-secrets set-integration <provider> --preflight-path /v1/me

In the dashboard, the provider’s Integration card edits the path and the logo. The logo is an uploaded PNG, JPEG or WebP image (the dashboard resizes it to 128px); end users see it on the hosted Connect page and in their wallet.

From here a custom provider behaves exactly like any catalog provider. Issue a grant binding it to a principal — system, user, group, or agent — then call the API with the SDK:

import asyncio, os
from alter_sdk import App, HttpMethod
async def main():
async with App(api_key=os.environ["ALTER_API_KEY"]) as app:
response = await app.request(
HttpMethod.GET,
"https://billing.internal.example.com/v1/invoices",
grant_id=os.environ["BILLING_GRANT_ID"],
)
print(response.status_code, response.json())
asyncio.run(main())
import { App, HttpMethod } from "@alter-ai/alter-sdk";
const app = new App({ apiKey: process.env.ALTER_API_KEY! });
try {
const response = await app.request(
HttpMethod.GET,
"https://billing.internal.example.com/v1/invoices",
{ grantId: process.env.BILLING_GRANT_ID! },
);
console.log(response.status, await response.json());
} finally {
await app.close();
}

Alter injects X-Api-Key: <key> and X-Workspace-Id: ws_12345 on the outgoing call. Grant issuance, per-user and group binding, rotation, and slug-based resolution all work as described in Provision secrets for backend services.

Operation and family content rules name an API’s operations. For a custom provider, the operations are yours:

  • From the API’s OpenAPI document. Name its https URL (up to 2 MB). Alter pulls it at once and again every day, and adopts its operations on every pull — new operations become nameable without a review step, and operations the document drops stop matching. Each pulled operation’s family comes from its method: GET is read, DELETE is delete, POST / PUT / PATCH are write.
  • By hand. Add an operation — an id, a method, a path template such as /loans/{loanId}, and one or more families (read, write, delete, send, admin, payment). An operation you add replaces a pulled one with the same id or the same route, which is how a pulled POST /loans/{loanId}/disclosures becomes send so a “no outbound messages” family rule binds it.
Terminal window
alter managed-secrets operations set-spec <provider> --url https://docs.example.com/openapi.json
alter managed-secrets operations set <provider> disclosures.send \
--method POST --path '/loans/{loanId}/disclosures' --family send
alter managed-secrets operations list <provider>

In the dashboard, the provider’s API operations card shows the document’s status and every operation, with Sync now, Add operation and Override.

Every request-shape rule type works on a custom provider’s traffic, at every level: method/endpoint restrictions (“GET only”, “/v1/invoices/** only”), attribute match rules, IP allowlists, rate limits, time windows, human-in-the-loop approvals, and per-request rules from an SDK constrained client.

Content rules match the operations you map (step 5): a rule that names an operation or a family applies to the requests that match it. Content rules are fail-closed, so an operation-scoped rule denies a request that matches no mapped operation, and denies everything on a provider that maps none. Custom operations carry no parameters, so a rule condition on a parameter (recipients, amounts) and field redaction cannot be judged on a custom provider and deny the request; express those with method/endpoint restrictions or attribute match rules.

Changing operations changes what rules bind to, and a remap can make a deny rule stop matching, so authoring them and naming or pulling the document need dashboard_secrets:operations. No wildcard grants it, dashboard_secrets:* and dashboard_secrets:admin included: mint it by typing the scope. Listing them needs dashboard_secrets:read.

Everything else — storage, grants, delegation, rotation, audit, request-shape policy rules — is identical. The differences:

Catalog templateCustom template
Injection contractProvider-verifiedOperator-configured
Allowed hostsOften supplied by the templateOperator-configured (required for proxied calls)
LogoThe template’sUploaded by the operator
Save-time credential verificationWhere the provider supports itAgainst the operator’s credential check path; otherwise skipped, and verified on first use
Content rules (operations / families)Where the provider has an operation catalogAgainst the operations the operator maps (pulled daily from an OpenAPI document, or added by hand)
Content rules (parameters / redaction)Where the provider’s catalog projects themNot available — such a rule denies
What an approver seesAttested operation and its security-relevant valuesEvery value the agent sends, by location (body.…, query.…, header.…), exactly as sent; Alter’s credential is added after approval and never shown, and a request with a value that cannot be shown faithfully is not approvable

Report an issue with this page

Necessary

Required for sign-in, security, authorization, and remembering your choices.

Always active

Analytics

Helps us understand which product and documentation features are useful.

Performance diagnostics

Uses performance tracing and privacy-masked session replay to diagnose problems.

You can change these choices at any time from Cookie settings.