Skip to main content
A protocol is the contract at the heart of Link. It declares a set of actions and the input and output shape of each — independent of any provider’s wire format. Callers code against the protocol; backends bind it to real providers. A protocol is a JSON document validated against Link’s meta-schema. Importing it stores the contract, generates an OpenAPI 3.1 specification for it, and makes its actions invokable.

Anatomy

  • $id — the canonical URL identifying this protocol. Backends reference the protocol by this URL, and it is the join key between a protocol and the backends that implement it.
  • name / version — human-readable identity and a version string.
  • actions — the operations the protocol exposes (see below).
  • components — optional reusable schema definitions referenced across actions.

Actions

An action is a named operation — for example assess. Each action declares:
  • A method — one of POST, GET, PUT, PATCH, DELETE. Callers must invoke the action with this method.
  • A description — a human-readable summary.
  • A request schema — the input the caller sends.
  • A set of responses — keyed by HTTP status code, each with an output schema.
Action names may use dot notation to namespace related operations — for example notify.authorization, notify.chargeback, notify.fraud_report. For the exact field-by-field definition, see the Protocol schema reference.

Variants and discriminators

An action’s request can be either:
  • Monomorphic — a single request schema, or
  • Polymorphic — a discriminated union. A discriminator field in the request selects which variant applies.
For example, the assess action might discriminate on credential.type: a request with credential.type: "pan" resolves to the assess.pan variant, while credential.type: "token" resolves to assess.token. Backends implement variants by their full key (assess.pan), and Link rejects a backend that misses a required action or variant.

Versioning

The protocol version and its $id together identify a contract. A new version is a new protocol document with its own $id — existing backends keep targeting the version they were bound to, so contract changes never silently alter live behavior. A publisher can also update the document at the same $id, for example to add an optional field. Link picks up such an update only when you refresh the protocol.

Refresh a protocol

A refresh fetches the protocol again from the URL it was imported from (its $id) and stores the fetched document as the protocol’s next revision. It requires the admin:protocols:write scope. Start with a dry run: it runs every check and writes nothing.
Send the request without a body, or with {"dry_run": false}, to store the update. status is one of: A refresh runs the same checks as an import (meta-schema, $ref resolution, required variants, schema compilation). It also checks every backend that references the protocol against the new document: the connections and response mappings of every backend, and the required actions and variants of enabled backends. If any check fails, nothing changes. Protocol summaries, in the protocol list and in the import and refresh responses, carry revision and updated_at. revision starts at 1 and grows by one with every refresh that stores a new document.

Refresh errors

Link does not follow redirects. The URL Link fetches must answer 200 with the JSON document itself. A URL that redirects, even to the same document, fails with FETCH_FAILED and a message naming the redirect status.The Specter v1 URL, https://developer.hellgate.io/protocols/specter/v1, redirects. Link therefore fetches Specter v1 from https://developer.hellgate.io/static/specter-v1.json, on import and on refresh. The protocol keeps its URL, and the fetched document’s $id must still equal it. The instance setting PROTOCOL_SOURCE_URLS holds this mapping from protocol URL to source URL; by default it names only Specter v1.

GraphQL

Link’s GraphQL API at /api/graphql offers the same operation as the refreshProtocol mutation, with the same scope, checks, and classifiers. It returns status in upper case, and errors in the errors array.

Specter protocol at startup

By default, when an instance starts and has no Specter protocol stored yet, Link imports it. Link refreshes a Specter protocol that is already stored at startup only when the instance setting SPECTER_AUTO_REFRESH_ENABLED is on. The setting is off by default, so a stored Specter protocol changes only when you refresh it. A startup refresh runs the checks described above and fetches from the same source URL.

Meta-schema

Every protocol is validated against the Link protocol meta-schema (link-protocol-v1) at import time. A protocol that does not conform is rejected. The meta-schema is served by each instance:
Standalone
On a Bundled instance, imports are restricted to the protocols that serve its Specter instance. To author and import your own protocols, you need a Standalone instance; see the Protocol schema reference.

Generated OpenAPI

Because a protocol fully describes its actions and schemas, Link generates an OpenAPI 3.1 specification for each imported protocol. This is the contract a consumer of the protocol integrates against — the invoke surface for that specific protocol — and is retrieved per protocol:
Link operates at the protocol level: the invoke API is defined by whichever protocol you import, not by a single fixed Link API. To document the endpoints your callers use, generate the OpenAPI for that protocol.

Next steps

Backends

Bind a protocol to a real provider with credentials, authentication, and mapping rules.

Invocation

Call a protocol action and handle the result.