> ## Documentation Index
> Fetch the complete documentation index at: https://developer.hellgate.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Backends

> A backend binds a protocol to a real provider: its host, credentials, authentication, and the declarative mapping rules that translate the contract into the provider's wire format.

A **backend** binds a [protocol](/products/link/concepts/protocols) to a real external provider. It supplies the
provider's host and credentials, an authentication strategy, and the **mapping rules** that translate each
protocol action into the provider's wire format — and the provider's response back into the protocol's shape.

One protocol can have many backends. That is the core of Link: define the contract once, then point it at as many
providers as you need, and switch or combine them without changing the caller.

<Note>
  In a Specter integration, a Link backend *is* the external risk engine Specter calls. Specter reaches it over
  the `specter-v1` protocol — see [Specter backends](/products/specter/concepts/backends).
</Note>

## Anatomy

```json theme={null}
{
  "id": "acme-risk",
  "name": "Acme Risk",
  "protocol": "https://developer.hellgate.io/protocols/specter/v1",
  "host": "api.acme.example",
  "enabled": true,
  "credentials": { "token": "secret_key_test_..." },
  "auth_pipeline": { "source_type": "inline", "credential_type": "bearer" },
  "connections": { "assess.pan": { "...": "..." } }
}
```

* **`id`** — a short identifier you choose, used to address the backend (for example when disambiguating an invocation).
* **`protocol`** — the protocol's `$id` URL; must match an imported protocol.
* **`host`** — the provider's hostname (no scheme, no path).
* **`credentials`** — consumed by the authentication pipeline; encrypted at rest.
* **`auth_pipeline`** — how outbound requests are authenticated (see below).
* **`connections`** — a map from action (or `action.variant`) to its mapping configuration.

<Warning>
  Backend hosts are checked by an SSRF guard when a backend is created or updated (imported protocol URLs are
  checked the same way). The URL must use `https`, the host must resolve, and **every** resolved IPv4 and IPv6
  address must fall outside the reserved ranges — private, loopback, link-local, multicast, carrier-grade NAT, and
  IPv6 tunneling prefixes (Teredo, 6to4, NAT64). The check runs at configuration time, not on each invocation.
</Warning>

## Authentication strategies

The `auth_pipeline` determines how Link authenticates outbound calls to the provider. A non-empty pipeline
requires a `source_type` (`inline` or `vault` — where the credentials come from) and a `credential_type`:

| `credential_type`              | Behavior                                                                                                  |
| ------------------------------ | --------------------------------------------------------------------------------------------------------- |
| *(omit the pipeline, or `{}`)* | No authentication added.                                                                                  |
| `basic`                        | HTTP Basic, from `username` / `password` credentials.                                                     |
| `bearer`                       | `Authorization: Bearer <token>`. Set `token_prefix` to use a different scheme word (for example `token`). |
| `hmac_sha256`                  | HMAC-SHA256 request signing, including a `Digest` over the request body.                                  |

```json theme={null}
"auth_pipeline": { "source_type": "inline", "credential_type": "bearer", "token_prefix": "token" }
```

Auth-pipeline headers (HMAC `Digest`, etc.) take precedence over mapped headers on conflict, so signing
guarantees are never overwritten.

### Credentials and secrets

Credentials are held either **inline** (encrypted at rest in the backend record) or pulled from a **secrets store**
through a pointer, so the secret value itself never lives in the backend document — Link fetches
it at request time. Inline credentials can be rotated in place with the rotate-credentials endpoint, without
recreating the backend.

## Provisioning: self vs. managed

A backend declares a provisioning mode:

* **`self`** (default) — you own and manage the backend and its credentials.
* **`managed`** — the backend is provisioned and operated by Hellgate on your behalf (an optional service). Its
  create, update, and delete operations require the additional `admin:managed-backends:write` scope on top of
  `admin:backends:write`.

Credentials are never returned by the admin read endpoints, regardless of provisioning mode.

## Mapping

Mapping is **declarative**: adapter logic lives in versioned templates, not in code. Each connection has a
`request_mapping` and a `response_mapping`.

### Template grammar

Mapping leaves are plain JSON values. Strings carry a **mustache-style grammar** — zero or more `{{ expr }}`
holes inside a literal string — where `expr` is a scoped path followed by an optional chain of filters:

```json theme={null}
"pan": "{{ $req.body.first_six | required }}XXXXXX{{ $req.body.last_four | required }}"
```

Paths are **scoped** to a root:

| Root            | Resolves to                              | Valid in                      |
| --------------- | ---------------------------------------- | ----------------------------- |
| `$req.body.*`   | The caller's invoke request body         | Request and response mappings |
| `$req.header.*` | The caller's request headers (downcased) | Request and response mappings |
| `$res.body.*`   | The provider's response body             | Response mappings only        |
| `$res.header.*` | The provider's response headers          | Response mappings only        |

Filters chain with `|`. Common filters include `required` (abort the call if the value is missing),
`default(<path or literal>)` (fall back to another value), `omit_if_null` (drop the key when null),
`map({ FROM: TO })` (translate enum values), `to_int`, and `first` / `last` / `prefix` for string slicing.

### Request mapping

`request_mapping` mirrors the provider's expected request. It has an optional `body` (a nested object matching
the provider's shape) and `headers` (a flat map of header name to template):

```json theme={null}
"request_mapping": {
  "body": {
    "amount": "{{ $req.body.transaction.amount | required }}",
    "currency": "{{ $req.body.transaction.currency | required }}"
  },
  "headers": {
    "x-correlation-id": "{{ $req.header.x-correlation-id | omit_if_null }}"
  }
}
```

### Response mapping

`response_mapping` is keyed by the provider's HTTP status (`200`, `4xx`, `default`, …). Each entry has a
`return` (the HTTP status Link returns to the caller) and a `body` template that produces the protocol-shaped
result:

```json theme={null}
"response_mapping": {
  "200": {
    "return": "200",
    "body": {
      "type": "enum",
      "value": "{{ $res.body.status | map({ ACCEPTED: ALLOW, REJECTED: BLOCK }) | required }}",
      "backend_reference": "{{ $res.body.id | omit_if_null }}"
    }
  },
  "4xx": {
    "return": "422",
    "body": { "type": "error", "source": "backend", "code": "{{ $res.body.error | omit_if_null }}" }
  }
}
```

Response `body` templates are validated at backend-write time against the protocol's response schema for the
given `return` code — missing required keys, unknown keys, and unmatched union branches are rejected before the
backend goes live.

### Connection keys

A connection key is either the bare action name (`assess`) or `action.variant` (`assess.pan`) when the action
has a discriminator. Link validates connections against the imported protocol and rejects a backend that misses a
required action or variant.

## Mocks

Instead of live mapping, a connection can declare **mock responses** — match conditions plus canned responses.
When present, invocations are matched against the mocks and served without any external call. Mocks are ideal for
local development, tests, and protocol fixtures.

```json theme={null}
"resolve": { "mocks": [ { "match": {}, "respond": { "type": "enum", "value": "ALLOW" } } ] }
```

## Message-level encryption

Some providers require every request and response body to be encrypted on top of TLS (JWE). A backend can carry
an `encryption` block that makes Link encrypt outbound bodies and decrypt inbound ones transparently, so callers
keep sending and receiving plain JSON. Encryption keys are managed separately and support rotation.

## Selecting a backend

When several enabled backends implement the same action and variant, the caller disambiguates with a `backend`
query parameter on invocation. See [Invocation](/products/link/concepts/invocation#selecting-a-backend).

## Next steps

<CardGroup cols={2}>
  <Card title="Invocation" icon="bolt" href="/products/link/concepts/invocation">
    Call an action, match the method, select a backend, and handle errors.
  </Card>

  <Card title="Protocols" icon="file-contract" href="/products/link/concepts/protocols">
    The contract a backend implements.
  </Card>
</CardGroup>
