---
title: FARP Registration API
description: The HTTP API services use to register with the Octopus gateway, send heartbeats, deregister, and read the federated schemas.
---

# FARP Registration API

This is the HTTP API exposed by the gateway's FARP handler (`octopus-farp`). Services use it to
register themselves, keep their registration alive with heartbeats, and deregister on shutdown.
The same handler serves the **federated schemas** (OpenAPI / AsyncAPI / GraphQL / gRPC) merged
from every registered service, plus Swagger UI and ReDoc.

For the concepts — how discovery, schema fetching, and route generation fit together — see
[Service Discovery & FARP](/docs/octopus/farp) and [Schema federation](/docs/octopus/farp/schema-federation).

## Path prefixes

The handler answers the same set of endpoints under several prefixes. The gateway rewrites each
prefix to a canonical `/farp/...` path before dispatching, so the endpoint behaviour is identical
whichever prefix you use:

| Prefix | Purpose |
|--------|---------|
| `/_farp/v1/...` | Canonical FARP v1 push protocol. Preferred for service self-registration. |
| `/__/farp/...` and `/__farp/...` | Internal management prefix. |
| `/farp/...` | Backwards-compatible legacy prefix. |

In addition, the root paths `/docs`, `/swagger`, and `/redoc` are routed to the FARP
documentation UIs.

<Callout type="info">
  The endpoint tables below are written with the canonical `/farp/...` paths. To call them over
  the v1 push prefix, swap `/farp` for `/_farp/v1` — for example `POST /_farp/v1/register`.
</Callout>

## Authentication

The FARP endpoints are not authenticated by the handler itself. Treat them as internal: restrict
access with network policy, run them on a non-public listener, or front them with an auth
middleware. Do not expose registration to untrusted clients.

---

## Service registration

### POST /farp/register

Register a service instance. The endpoint accepts **two** request shapes and picks the right one
by inspecting the JSON body: if it contains an `instance` field it is treated as a FARP v1 push
registration, otherwise it falls back to the legacy manifest form.

<Tabs items={['FARP v1 push', 'Legacy']}>
<Tab value="FARP v1 push">

The v1 push payload carries instance info and, optionally, the manifest. If `manifest` is
omitted, the gateway fetches it from the instance's `/_farp/manifest` endpoint (resolved from the
`farp.manifest` metadata URL, or built from the instance address). The gateway then fetches the
schemas the manifest references, registers the upstream and routes, and federates.

```http
POST /farp/register
Content-Type: application/json
```

```json
{
  "instance": {
    "id": "user-service-7f9c",
    "service_name": "user-service",
    "service_version": "1.0.0",
    "address": "10.0.0.12",
    "port": 8080,
    "tags": ["prod"],
    "metadata": {
      "farp.manifest": "http://10.0.0.12:8080/_farp/manifest"
    },
    "status": "up"
  },
  "manifest": null
}
```

Only `id`, `service_name`, `address`, and `port` are required; `service_version`, `tags`,
`metadata`, and `status` are optional. When `manifest` is `null` (or absent) the gateway fetches
it from the service.

**Response — `201 Created`** (a push registration acknowledgement):

```json
{
  "status": "registered",
  "routes_checksum": "a1b2c3d4...",
  "schemas_applied": 2
}
```

`routes_checksum` is the route-table checksum the gateway applied, and `schemas_applied` is the
number of schemas it fetched and stored. Services compare `routes_checksum` against their own on
heartbeat to detect drift.

</Tab>
<Tab value="Legacy">

The legacy payload supplies the manifest directly, with optional inline schema descriptors. If
schemas are supplied, the gateway federates them and generates routes immediately.

```http
POST /farp/register
Content-Type: application/json
```

```json
{
  "manifest": {
    "service_name": "user-service",
    "service_version": "1.0.0",
    "instance_id": "user-service-7f9c"
  },
  "schemas": [
    {
      "id": "user-service-openapi",
      "service": "user-service",
      "format": "openapi",
      "version": "3.1.0",
      "content": "{ ...openapi document... }"
    }
  ]
}
```

`schemas` is optional. Each descriptor's `format` is one of `openapi`, `asyncapi`, `grpc`,
`graphql`, or `custom`. Route generation runs for `openapi`, `asyncapi`, and `graphql`; `grpc`
and `custom` are rejected with an "unsupported schema format" error (see
[Schema federation](/docs/octopus/farp/schema-federation) for the full support matrix).

**Response — `201 Created`**:

```json
{
  "success": true,
  "message": "Service 'user-service' registered successfully",
  "routes": [
    { "method": "GET", "path": "/users", "upstream_name": "user-service" }
  ]
}
```

</Tab>
</Tabs>

<Callout type="warn">
  The manifest is validated against the FARP JSON schema before it is stored. An invalid manifest
  (missing required fields, a bad schema checksum, an unrecognised format) is rejected.
</Callout>

---

### PUT /farp/heartbeat/\{instance_id\}

Keep a registered instance alive (FARP v1 push protocol). Send periodically from the service.

```http
PUT /farp/heartbeat/user-service-7f9c
Content-Type: application/json
```

```json
{
  "status": "up",
  "routes_checksum": "a1b2c3d4..."
}
```

`routes_checksum` is optional. When present, the gateway compares it against its own copy; if
they differ (or the gateway holds no schemas), it re-fetches the manifest so the two sides
reconcile.

**Response — `200 OK`**:

```json
{
  "status": "ok",
  "routes_checksum": "a1b2c3d4...",
  "schemas_applied": 2
}
```

If the gateway has no record of the instance — for example after a restart that lost in-memory
state — it returns **`404 Not Found`** with a re-registration hint:

```json
{
  "error": "instance not found",
  "action": "re-register",
  "message": "Gateway has no record of this instance. Please re-register with full manifest."
}
```

---

### DELETE /farp/deregister/\{instance_id\}

Deregister a single instance by ID (FARP v1 push protocol). Send on graceful shutdown.

```http
DELETE /farp/deregister/user-service-7f9c
```

**Response — `200 OK`**:

```json
{ "status": "deregistered" }
```

Returns **`404 Not Found`** (`{"error": "instance not found"}`) if the instance is unknown.

---

### PUT /farp/services/\{service_name\}

Update an existing service registration with a new manifest (legacy form). The body is a
manifest registration request; the manifest is validated before it replaces the stored one.

```http
PUT /farp/services/user-service
Content-Type: application/json
```

```json
{
  "manifest": {
    "service_name": "user-service",
    "service_version": "1.1.0",
    "instance_id": "user-service-7f9c"
  }
}
```

**Response — `200 OK`**:

```json
{
  "success": true,
  "message": "Service 'user-service' updated successfully"
}
```

---

### DELETE /farp/services/\{service_name\}

Deregister a service by name (removes the whole service, not a single instance).

```http
DELETE /farp/services/user-service
```

**Response — `200 OK`**:

```json
{
  "success": true,
  "message": "Service 'user-service' deregistered successfully"
}
```

---

## Service discovery

### GET /farp/services

List all registered services.

```bash
curl http://gateway:8080/__/farp/services
```

**Response — `200 OK`**:

```json
{
  "services": [
    {
      "name": "user-service",
      "version": "1.0.0",
      "schema_count": 1,
      "updated_at": "1699123456"
    }
  ]
}
```

`updated_at` is a Unix timestamp (seconds) as a string, and is omitted when unavailable.

---

### GET /farp/services/\{service_name\}

Get the full record for one service — its manifest, stored schemas, and timestamps.

```bash
curl http://gateway:8080/__/farp/services/user-service
```

**Response — `200 OK`**:

```json
{
  "service_name": "user-service",
  "manifest": { "service_name": "user-service", "service_version": "1.0.0" },
  "schemas": [],
  "registered_at": 1699123456,
  "updated_at": 1699123789
}
```

Returns **`404 Not Found`** if the service is not registered.

---

## Federated schemas

The gateway merges the schemas of all registered services into a single federated document per
format. Structural merging applies to OpenAPI and AsyncAPI; GraphQL and gRPC are concatenated.
See [Schema federation](/docs/octopus/farp/schema-federation) for the merge behaviour and limits.

All of these read endpoints respond `200 OK`. When no services are registered they return a valid
empty document rather than an error.

### GET /farp/openapi.json

The merged OpenAPI document (`application/json`). When a router is wired in, gateway routes that
are missing from the federated spec are supplemented into it.

```bash
curl http://gateway:8080/__/farp/openapi.json
```

### GET /farp/asyncapi.json

The merged AsyncAPI document (`application/json`).

### GET /farp/graphql

The merged GraphQL schema (`text/plain`).

### GET /farp/grpc

The merged gRPC protobuf (`text/plain`).

### GET /farp/schemas

List which federated formats are currently available and their source services.

```json
{
  "schemas": [
    {
      "format": "openapi",
      "sources": ["user-service", "post-service"],
      "updated_at": 1699123456
    }
  ]
}
```

### GET /farp/schema/\{format\}

Fetch one federated schema by format. `{format}` is `openapi`, `asyncapi`, `graphql`, or `grpc`.
OpenAPI and AsyncAPI return `application/json`; GraphQL and gRPC return `text/plain`.

- `400 Bad Request` — `{"error": "invalid_format", ...}` for an unknown format.
- `404 Not Found` — `{"error": "not_found", ...}` if that format has not been federated.

### POST /farp/federate

Manually trigger federation of all registered service schemas.

```bash
curl -X POST http://gateway:8080/__/farp/federate
```

**Response — `200 OK`**:

```json
{
  "success": true,
  "message": "Schema federation completed",
  "schemas_processed": 3,
  "formats_generated": ["openapi", "asyncapi"],
  "services": ["user-service", "post-service"]
}
```

When there is nothing to federate it returns `{"success": true, "message": "No schemas to federate", "schemas_processed": 0}`.

---

## Documentation UIs

### GET /farp/docs

Serves a Swagger UI page (HTML) pointed at the federated `/farp/openapi.json`. Also reachable at
the root path `/docs` (and `/swagger`).

### GET /farp/redoc

Serves a ReDoc page (HTML) for the federated OpenAPI document. Also reachable at the root path
`/redoc`.

---

## Errors

Unmatched routes return **`404 Not Found`**:

```json
{
  "error": "not_found",
  "message": "Endpoint not found"
}
```

Other failures (malformed JSON, an invalid manifest, a failed registration) return an error
status with a message describing the cause.

---

## Next steps

- [Service Discovery & FARP](/docs/octopus/farp) — the protocol and discovery backends.
- [Schema federation](/docs/octopus/farp/schema-federation) — which formats generate routes and how merging works.
- [FARP configuration](/docs/octopus/configuration/farp) — enabling FARP and configuring discovery.
- [Admin REST API](/docs/octopus/observability/admin-api) — the gateway's management API.
