If the API you need isn’t in the supported services list, connect it with one of seven generic services: HTTP Bearer Token, HTTP Basic Authentication, Custom Token Authentication, HTTP OAuth 2.0 Bearer, HTTP OAuth 1.0a, HTTP HMAC signature, or HTTP AWS Signature v4. You enter the credential once in the dashboard, and your function calls the API with ctx.http.authed('alias') like any other connected service. The platform adds the credential to the request, so the secret never reaches your code.
This page walks through the whole path with a made-up credential against a public test API, so you can run it without a third-party account.
Pick a generic service
| If the API expects | Pick |
|---|---|
Authorization: Bearer <token> | HTTP Bearer Token |
| A username and password, or an API token used as the password | HTTP Basic Authentication |
| A token or API key in a header you name, or in the query string | Custom Token Authentication |
| An OAuth 2.0 token from a client ID and client secret (the client credentials grant) | HTTP OAuth 2.0 Bearer |
| An OAuth 1.0a signed request | HTTP OAuth 1.0a |
A signature computed over the request, such as X-Signature: ... | HTTP HMAC signature |
| An AWS Signature v4 signed request | HTTP AWS Signature v4 |
HTTP OAuth 2.0 Bearer covers only the client credentials grant, where your server exchanges a client ID and secret for a token and Helix refreshes it for you. OAuth 2.0 with a user sign-in is not a generic service. If the service offers it, use its own entry in the supported services list, which handles sign-in and refresh.
What to enter in the form
| Service | Fields |
|---|---|
| HTTP Bearer Token | Token |
| HTTP Basic Authentication | Username, Password |
| Custom Token Authentication | Token, Location, Location Value, Pin to domain (optional) |
| HTTP OAuth 2.0 Bearer | Client ID, Client Secret, Token URL. See OAuth 2.0 client credentials below |
| HTTP OAuth 1.0a, HTTP HMAC signature, HTTP AWS Signature v4 | Described in signed requests below |
For Custom Token Authentication:
| Field | Value |
|---|---|
| Token | The API key or token |
| Location | header (the default) or query for the query string |
| Location Value | The header name, such as X-Api-Key, or the query parameter name, such as api_key |
| Pin to domain | Optional, and recommended. The only domain this credential may be used with, such as api.vendor.com. Subdomains are included. Enter the bare domain: no https://, path, wildcard, or port |
OAuth 2.0 client credentials
HTTP OAuth 2.0 Bearer is for APIs that issue tokens from a client ID and client secret. You enter the credentials and the token URL once. Helix requests the token, stores it, refreshes it when it expires, and sends Authorization: Bearer <token> on every call.
| Field | Value |
|---|---|
| Client ID | The client ID the API issued to you |
| Client Secret | The client secret the API issued to you |
| Token URL | The full URL of the token endpoint, such as https://login.vendor.com/oauth2/token |
The token URL does not have to be on the same host as the API. If tokens come from login.vendor.com and you call api.vendor.com, enter the token URL as above and pass the API URL in your code as usual.
When you create the authentication you also choose how the API expects the client to identify itself at the token endpoint:
| Choice | Pick it when the API expects |
|---|---|
| Client credentials (credentials in request body), the default | The client ID and secret as form fields in the token request |
| Client credentials (HTTP Basic client authentication) | The client ID and secret in an Authorization: Basic header on the token request |
Your API’s documentation says which. If one is refused, the other is the one to try.
Two limits to know:
- No domain restriction. Unlike the signed services below, this credential is not limited to one domain, so it is sent to whatever host your code calls with this alias. Use an alias only for the API you connected it to.
- No scope field. The token is requested without a scope. If the API requires one, this service can’t connect it yet.
Any other header the API needs, such as a profile or tenant header, is set by your code on the request. Helix only adds the Authorization header.
Signed requests
OAuth 1.0a, HMAC, and AWS Signature v4 sign each request instead of sending a fixed token. You enter the secrets once, and the platform computes the signature on every call.
Pin to domain is required
A signed credential can only be used against the domain you pin it to, and its subdomains. api.vendor.com allows eu.api.vendor.com, not vendor.com. A request to any other host fails instead of sending the credential. Enter the bare domain: no https://, path, wildcard, or port.
These services also refuse to send a request when a required value is missing.
HTTP OAuth 1.0a
| Field | Value |
|---|---|
| Consumer key | The key issued to your application |
| Consumer secret | The secret issued to your application |
| Token | Optional. Leave Token and Token secret empty for two-legged OAuth |
| Token secret | Optional, as above |
| Signature method | HMAC-SHA1 (the default) or HMAC-SHA256 |
| Pin to domain | Required |
You supply the credentials. Helix signs each request with them but doesn’t run the OAuth 1.0a token exchange, so for an API that needs a user’s access token you must already hold the token and token secret.
Not supported:
- The
realmparameter - The
RSA-SHA1andPLAINTEXTsignature methods - Requests with a form-urlencoded body
HTTP HMAC signature
| Field | Value |
|---|---|
| Secret | The shared secret the API verifies against |
| Algorithm | sha1, sha256 (the default), or sha512 |
| Signature encoding | hex (the default) or base64 |
| Header name | The header that carries the signature, such as X-Signature |
| Signature format | The header value, which must contain {{signature}}, such as sha256={{signature}} |
| Sign the request body | true (the default) or false |
| Signed headers | Optional, comma-separated, in order. Your function sets these headers on the request |
| Pin to domain | Required |
The signature covers the exact bytes your function sends. If the API verifies a re-serialized version of the body, the signatures won’t match. Sign the body, or list at least one signed header.
HTTP AWS Signature v4
| Field | Value |
|---|---|
| Access key | The AWS access key ID |
| Secret key | The AWS secret access key |
| Session token | Optional, for temporary credentials |
| Signing service name | Optional. Only needed when the host doesn’t name the service |
| Region | Optional. Only needed when the host doesn’t name the region |
| Pin to domain | Required, and prefilled with amazonaws.com |
The region and service come from a regional host such as sts.us-east-1.amazonaws.com. Set them yourself when the host doesn’t name them, or names a different service than the one that signs. For example, bedrock-runtime signs as bedrock.
Worked example
This example uses httpbin.org, a public API that echoes back what it receives. The token below is made up. Don’t enter a real secret while you try this.
1. Create the authentication
helix auth connect --new
The command opens the dashboard’s connect page for your workspace. Add --no-open to print the link instead. In the dashboard, choose HTTP Bearer Token, enter demo-token-123 as the token, and name the authentication.
Back in the terminal, the CLI finds the new authentication and writes an alias into helix.config.ts. The CLI suggests an alias from the service name, so here it is http_bearer_token. You can type a different one at the prompt, using lowercase letters, digits, and underscores, starting with a letter. Without a terminal, such as in an agent loop, the command only prints the link. Once you’ve created the authentication, wire it up with helix auth connect or helix auth add <alias> <uuid>.
2. Call the API
// functions/api/v1/demo.get.ts
import { defineFunction } from '@trayai/helix-sdk';
export default defineFunction(async (ctx) => {
const res = await ctx.http
.authed('http_bearer_token')
.get('https://httpbin.org/bearer');
return res.body;
});
An authed call returns { status, body, headers }. body is already parsed JSON for a JSON response. A non-2xx answer from the API is returned, not thrown, so check res.status when you need to handle errors.
3. Run it
helix dev
curl http://localhost:3000/api/v1/demo
Success looks like this:
{ "authenticated": true, "token": "demo-token-123" }
The test API received Authorization: Bearer demo-token-123, which your function never wrote.
Try the other two
The same steps work with the other generic services. Change the alias and the URL in step 2, and return res.body as before.
| Service | Entered in the form | Call | Response shows |
|---|---|---|---|
| HTTP Basic Authentication | Username tester, password s3cret-pass | https://httpbin.org/basic-auth/tester/s3cret-pass | {"authenticated": true, "user": "tester"} |
| Custom Token Authentication | Location query, Location Value api_key | https://httpbin.org/anything | args contains api_key with your token |
| Custom Token Authentication | Location header, Location Value X-Api-Key | https://httpbin.org/headers | The X-Api-Key header with your token |
Try the signed services
Each example uses a public test API and made-up or published test keys, so you need no real account. Create the authentication as in step 1 above. The aliases below are the CLI’s suggested defaults (http_oauth1, http_hmac, http_aws_sigv4), and you can edit them at the prompt. Call each from a function that returns the result:
// functions/api/v1/signed.get.ts
import { defineFunction } from '@trayai/helix-sdk';
export default defineFunction(async (ctx) => {
const res = await ctx.http.authed('http_oauth1').get('https://postman-echo.com/oauth1');
return { status: res.status, body: res.body };
});
OAuth 1.0a
Postman Echo checks the signature with its published test key.
| Field | Value |
|---|---|
| Consumer key | RKCGzna7bv9YD57c |
| Consumer secret | D+EdQ-gs$-%@2Nu7 |
| Token and Token secret | Leave empty |
| Signature method | HMAC-SHA1 |
| Pin to domain | postman-echo.com |
Call GET https://postman-echo.com/oauth1. Success is status 200 with this body:
{ "status": "pass", "message": "OAuth-1.0a signature verification was successful" }
HMAC signature
| Field | Value |
|---|---|
| Secret | dummy-hmac-secret |
| Algorithm | sha256 |
| Signature encoding | hex |
| Header name | X-Signature |
| Signature format | {{signature}} |
| Sign the request body | true |
| Signed headers | Leave empty |
| Pin to domain | httpbin.org |
Send a JSON body to the echo endpoint:
const res = await ctx.http
.authed('http_hmac')
.post('https://httpbin.org/anything', { body: { event: 'order.created', id: 42 } });
The echo shows the exact string sent in body.data and the signature in body.headers['X-Signature']. For this body and secret the signature is eec200cc0004a0c3ceb9aeb1a4ddea237a549f1f65a11c40352347cbfdc394b4. Check it yourself:
printf '%s' '{"event":"order.created","id":42}' | openssl dgst -sha256 -hmac 'dummy-hmac-secret'
AWS Signature v4
| Field | Value |
|---|---|
| Access key | AKIDEXAMPLE |
| Secret key | wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY |
| Session token | Leave empty |
| Pin to domain | amazonaws.com |
These are the example keys from AWS’s own documentation, so AWS rejects them. Call GET https://sts.us-east-1.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15. Success for this exercise is status 403 with <Code>InvalidClientTokenId</Code> in the body. That shows AWS parsed a correctly formed signed request and rejected only the key. An unsigned request returns MissingAuthenticationToken instead. This does not prove the signature is valid, only that it is well formed. With a real key, the same call returns your account details.
Troubleshooting
The API answers 401. The call doesn’t throw: the 401 comes back as res.status === 401. The values or the location are wrong. Check the Token, Location, and Location Value you entered against what the API’s documentation asks for.
You want to see what is sent. Point a made-up credential at an echo endpoint such as https://httpbin.org/anything. Never do this with a real secret, because the echo site would receive it.
An OAuth 2.0 authentication won’t connect or the API answers 401. Check the Token URL first: it must be the token endpoint, not the API’s base URL. Then try the other client authentication choice in the table above. The credentials are sent to the token URL you entered, so a typo there can also leak the client secret to the wrong host. Re-enter the authentication if you suspect it.
A signed request is refused. HTTP OAuth 1.0a, HTTP HMAC signature, and HTTP AWS Signature v4 send nothing when a required value is missing or the host is outside the pinned domain. Check the form values and the Pin to domain against the URL you call.
A missing alias error. The alias isn’t in helix.config.ts. The error lists the aliases that are configured, so compare it with the name your code uses.
For how aliases, refresh, and request logging work, see connected services.