SalesOS

Named credentials

Store an outbound connection once — base URL plus authentication — and call it from functions, custom actions, flows and webhooks without your code ever seeing the secret.

A named credential is one of your organization's outbound connections: an apiName, an https base URL and how to authenticate to it. Code and configuration refer to it by name; SalesOS adds the authentication on its side for every call, keeps the call under the base URL and logs it. Rotating a partner's key means editing one credential.

Manage them under Settings → Developer → Connections, or with /v1/named-credentials (org owners and admins, session only; part of API access). Secrets are write-only: they are encrypted at rest and never returned by any endpoint — a credential shows hasSecret / missingSecrets instead.

curl -X POST https://api.salesos.org/v1/named-credentials \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{
    "apiName": "erp",
    "label": "ERP",
    "baseUrl": "https://erp.example.com/api",
    "authType": "API_KEY_HEADER",
    "headerName": "X-Api-Key",
    "secret": "…",
    "staticHeaders": { "Accept": "application/json" }
  }'

Authentication

authTypeConfigurationSent on each call
NONE—nothing
API_KEY_HEADERheaderName, secret<headerName>: <secret>
BEARERsecretAuthorization: Bearer <secret>
BASICusername, secret (the password)Authorization: Basic …
OAUTH2_CLIENT_CREDENTIALStokenUrl, clientId, clientSecret, optional scopesAuthorization: Bearer <access token>

For client credentials SalesOS posts grant_type=client_credentials (with client_id, client_secret and scope) to the token URL, reuses the access token until shortly before expires_in, and on a 401 from the API fetches a new token and retries once. staticHeaders are plain, non-secret headers (they cannot set Authorization, Cookie, Host or X-SalesOS-*). A credential that needs a secret cannot be turned on until it is set.

Where you use it

FromHow
Functionsawait fetch('callout:erp/orders?status=open') or await salesos.callout('erp', '/orders', { method: 'POST', body: { id } }). The base URL is the allow-list — no allowedHosts entry needed — and the function never sees the secret.
Custom actionsnamedCredentialId + calloutPath instead of url. The signed request goes to the base URL plus the path, with the credential's auth added.
FlowsThe HTTP_CALLOUT element: credential, method, path and body templates, response fields into variables.
WebhooksnamedCredentialId on a webhook whose URL starts with the base URL: every delivery carries the credential's authentication as well as the signature.

What SalesOS enforces

  • SSRF-safe: https only; no credentials in URLs; internal hostnames and private, internal or reserved addresses are refused, checked against the address actually connected to, on every redirect.
  • Stays under the base URL: a path may not be an absolute URL, start with //, or contain .., encoded slashes or control characters, and every redirect is re-checked against the base URL's origin and path — the authentication is never sent anywhere else.
  • Secrets don't follow a moved credential: changing the base URL's host (or the token URL) needs the secret entered again (400 CREDENTIAL_SECRET_REQUIRED), and a metadata deploy that moves it clears the secret and turns the credential off. The management API is session-only — API keys and OAuth tokens can't reach it.
  • Timeouts: the credential's timeout (1–10 s), capped by the caller's own deadline (a function's remaining time, a webhook's 10 s).
  • Logged: every call writes a row with the source (function, action, flow, webhook, test), method, path without the query string, status, latency and error code — never headers, bodies or secrets. See them under Calls on the credential, or GET /v1/named-credentials/:id/calls (kept 30 days).
  • Test: POST /v1/named-credentials/:id/test { method, path } makes one real call and returns the status, response headers and the first 4 KB.
  • In use: a credential that a custom action or webhook still references can't be deleted (409 CREDENTIAL_IN_USE). A callout through a credential whose secret was never set fails with CREDENTIAL_SECRET_MISSING.

Metadata and apps

Credentials move between orgs as NAMED_CREDENTIAL components (credentials/<apiName>.credential.json) with their configuration only: secrets never move. A new credential that needs a secret arrives inactive and the deploy result names the secret to set; an update keeps the target's secrets and on/off state. App manifests can declare credentials under namedCredentials (no secrets allowed): they install inactive and the installing admin sets the secret under Connections, where the app's credential accepts only its secrets and on/off.

On this page