Aegis Orchestrator
SEAL Gateway

Registering API Specs

Register OpenAPI 3.0 documents with the SEAL Gateway so workflows and the API Explorer can call HTTP operations on your behalf.

Registering API Specs

An API spec is the gateway's source of truth for an external HTTP API. It is an OpenAPI 3.0 document plus the metadata the gateway needs to talk to that API on behalf of an agent: a base URL, a credential resolution path, and an internal table of operations keyed by operationId.

You register a spec once. After that, workflows reference it by ID, and the API Explorer can issue ad-hoc requests against any operation in it.

An API spec on its own does nothing. It is a passive description. The gateway only makes outbound calls when a workflow step or an explorer request names an operation defined in a registered spec.


The shape of a spec

When you POST to /v1/specs, the gateway accepts the following request body:

FieldTypeRequiredPurpose
namestringyesHuman-readable label. Must be non-empty.
base_urlstringyesAbsolute URL prefix prepended to every operation path. Trailing slashes are trimmed.
inline_jsonobjectone ofThe full OpenAPI document inlined in the request body.
source_fetch_urlstringone ofA URL the gateway will GET to retrieve the OpenAPI document.
source_urlstringnoA stable identifier for the upstream document. If a spec with the same source_url already exists, registration is deduplicated and returns the existing spec's ID.
credential_pathobjectyesOne of the five credential resolution strategies. Tells the gateway how to obtain credentials at invocation time.

Exactly one of inline_json or source_fetch_url must be supplied. If both are missing the gateway returns 400 Validation.

After registration the gateway parses the OpenAPI document, indexes every operationId together with its method and path, and stores the parsed operations map on the spec. Operations without an operationId are dropped — give every operation an explicit operationId in the source document.


Inline vs source_fetch_url

Use inline (inline_json) whenUse source_fetch_url when
The OpenAPI document is generated by your build pipeline and you commit it.The upstream vendor publishes a stable URL (Petstore, Stripe, GitHub, etc.).
You want byte-for-byte reproducibility — the document is captured at registration time.You want the gateway to resolve the document fresh from upstream.
The document is small enough to send in a single HTTP request.The vendor updates the document independently and you accept a snapshot at registration.

In both cases the document is frozen on the spec record once registered. Re-fetching does not happen automatically — re-register the spec to pick up upstream changes (see Versioning below).


Example: register the Petstore OpenAPI

The Swagger Petstore is the canonical OpenAPI 3.0 reference document and is convenient for end-to-end testing. Register it with a StaticRef credential pointing at an OpenBao KV entry that holds an API key.

curl -X POST https://gateway.example.com/v1/specs \
  -H "Authorization: Bearer $OPERATOR_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "petstore",
    "base_url": "https://petstore3.swagger.io/api/v3",
    "source_fetch_url": "https://petstore3.swagger.io/api/v3/openapi.json",
    "source_url": "https://petstore3.swagger.io/api/v3/openapi.json",
    "credential_path": {
      "StaticRef": {
        "key": "kv/data/petstore/api-key"
      }
    }
  }'

A successful response returns the spec's UUID:

{ "id": "8b3a4c1e-9f2d-4a1c-b0a7-2e5f7d6c1a44" }

If a spec with the same source_url was registered earlier, the response is the existing record's ID with a deduplication marker:

{ "id": "8b3a4c1e-9f2d-4a1c-b0a7-2e5f7d6c1a44", "deduplicated": true }

Example: register an internal Terraform Cloud spec

Terraform Cloud's REST API is a private, dynamic-credential target — the right credential strategy is SystemJit, which mints a short-lived token from an OpenBao secrets engine on every call.

curl -X POST https://gateway.example.com/v1/specs \
  -H "Authorization: Bearer $OPERATOR_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "terraform-cloud",
    "base_url": "https://app.terraform.io/api/v2",
    "inline_json": {
      "openapi": "3.0.0",
      "info": { "title": "Terraform Cloud", "version": "v2" },
      "paths": {
        "/runs": {
          "post": {
            "operationId": "create_run",
            "summary": "Create a new run",
            "responses": { "201": { "description": "Created" } }
          }
        },
        "/runs/{run_id}": {
          "get": {
            "operationId": "get_run",
            "parameters": [
              { "name": "run_id", "in": "path", "required": true,
                "schema": { "type": "string" } }
            ],
            "responses": { "200": { "description": "OK" } }
          }
        },
        "/plans/{plan_id}/json-output": {
          "get": {
            "operationId": "get_plan_output",
            "parameters": [
              { "name": "plan_id", "in": "path", "required": true,
                "schema": { "type": "string" } }
            ],
            "responses": { "200": { "description": "OK" } }
          }
        }
      }
    },
    "credential_path": {
      "SystemJit": {
        "openbao_engine_path": "terraform-cloud",
        "role": "automation-runner"
      }
    }
  }'

The OpenBao engine at terraform-cloud/ issues bearer tokens for the automation-runner role, the gateway exchanges fresh per-invocation, and never persists the resulting token.


Lifecycle

Once registered, a spec is queryable, listable, and deletable:

EndpointMethodPurpose
/v1/specsGETList specs visible to the caller's tenant (tenant-scoped specs plus system-global ones).
/v1/specs/{id}GETReturn the full spec record, including the parsed operations map and the raw OpenAPI document.
/v1/specs/{id}DELETERemove the spec.

List

curl -H "Authorization: Bearer $OPERATOR_JWT" \
  https://gateway.example.com/v1/specs

The list endpoint returns a summary projection (id, name, source_url) — the full OpenAPI document is omitted for transfer efficiency. Fetch a single spec by ID to retrieve the full record.

Get

curl -H "Authorization: Bearer $OPERATOR_JWT" \
  https://gateway.example.com/v1/specs/8b3a4c1e-9f2d-4a1c-b0a7-2e5f7d6c1a44

Delete

curl -X DELETE \
  -H "Authorization: Bearer $OPERATOR_JWT" \
  https://gateway.example.com/v1/specs/8b3a4c1e-9f2d-4a1c-b0a7-2e5f7d6c1a44

Deletion semantics. The current implementation deletes the spec record without checking for workflows that still reference it. A workflow whose api_spec_id no longer resolves will fail at invocation time with a NotFound("api spec not found for workflow") error — the workflow itself is left in place. Either delete dependent workflows first, or be ready to re-register the spec if you need to recover.


Versioning

The gateway has no built-in versioning for API specs. Treat the combination of name and id as immutable. There is no PUT /v1/specs/{id} endpoint — the only way to change a spec is to register a new one.

To upgrade a spec:

  1. Register the new OpenAPI document under a new name (e.g. petstore-v3.1) and capture the new ID.
  2. Update each workflow that referenced the old spec to point at the new ID. Because there is no PUT /v1/workflows/{id} either, this means re-registering the workflow under the new ID.
  3. Delete the old spec when no workflows reference it.

If you maintain a stable source_url and only need to refresh the upstream document, registering with the same source_url is the wrong path — it deduplicates and returns the existing record. Pick a different source_url value (for example by appending a version) when you intentionally want to register a fresh snapshot.


Tenant scope

Every spec is registered against the caller's tenant. A spec with tenant_id = null is system-global and visible to every tenant; one with a populated tenant_id is visible only to that tenant. The list endpoint already filters by tenant, so there is no manual scoping to do — what you see is what your tenant can use.

System-global specs are typically registered out-of-band by a platform operator with a service-account JWT that carries no tenant claim.


Audit events

Every successful registration emits an ApiSpecRegistered event into the gateway's event store, recording:

  • the spec ID
  • the spec name
  • the registering operator
  • the registration timestamp

These events are visible through the gateway's Observability surface and are the audit trail for spec lifecycle. Deletion is not currently emitted as an event — track removals through Postgres directly if you need a hard audit record of disappearances.


Common errors

StatusCause
400 ValidationNeither inline_json nor source_fetch_url provided; name empty; base_url empty; OpenAPI document had no operations with operationId.
400 HttpThe gateway tried to fetch source_fetch_url and the upstream returned an error or non-JSON body.
401 UnauthorizedMissing or invalid operator JWT.
404 NotFoundGET or DELETE against an ID that no longer exists.

Next steps

  • Authoring Workflows — chain operations from a registered spec into a single named tool.
  • API Explorer — issue an ad-hoc request against any operation in a registered spec and slice the response with JSONPath.
  • Credential Resolution — pick the right credential_path strategy for your spec.

On this page