# Services API

> List, create, deploy and delete a project's services, and manage each one's variables and database access.

Source: https://docs.fortly.com/api/services

A service is a separately deployed part of a project, with its own address, releases and variables. See [Services](https://docs.fortly.com/deploy/services) for how they work. `{name}` is the project's `name` and `{service}` is a service `id`.

- `GET /projects/{name}/services` — The project's services
- `POST /projects/{name}/services` — Create a service
- `GET /projects/{name}/services/{service}` — One service
- `PATCH /projects/{name}/services/{service}` — Rename it or change its database access
- `DELETE /projects/{name}/services/{service}` — Delete it
- `POST /projects/{name}/services/{service}/deploys` — Deploy an archive or its saved GitHub folder
- `GET /projects/{name}/services/{service}/deployments` — Its releases
- `GET /projects/{name}/services/{service}/deployments/{id}/logs` — One release's build output
- `GET /projects/{name}/services/{service}/variables` — Its variables — names only
- `POST /projects/{name}/services/{service}/variables` — Set a variable
- `DELETE /projects/{name}/services/{service}/variables/{key}` — Remove a variable

## List services

```bash
curl https://api.fortly.com/projects/ormi-ada/services \
  -H "Authorization: Bearer $FORTLY_TOKEN"
```

```json
{
  "services": [
    {
      "id": "svc_0bf6…",
      "name": "Application",
      "kind": "application",
      "is_default": true,
      "host": "ormi-ada.fortly.com",
      "url": "https://ormi-ada.fortly.com",
      "active_deployment_id": "dpl_91c2…",
      "database_access": true,
      "revision": 1
    },
    {
      "id": "svc_df8e…",
      "name": "API",
      "kind": "web",
      "is_default": false,
      "host": "api--ormi-ada.fortly.com",
      "url": "https://api--ormi-ada.fortly.com",
      "active_deployment_id": "dpl_7662…",
      "database_access": false,
      "revision": 3
    }
  ],
  "capabilities": { "metadata_writes": true, "service_deployments": true, "can_delete_services": true }
}
```

The service with `is_default: true` is the project's application. Its releases and variables are the project's own (see [Deploys](https://docs.fortly.com/api/deploys) and [Secrets](https://docs.fortly.com/api/secrets)). `revision` changes with every edit, and the write endpoints below require it.

`can_delete_services` reflects the signed-in caller's current workspace role and the installation's write capability. It is true for owners/admins when service metadata writes are enabled and false for members. Use it to present management actions; it is not authorization by itself. The DELETE endpoint rechecks ownership, role, target, name and revision on every request.

## Create a service

- `name` (string, required) — 1–48 characters, unique within the project.
- `kind` (string, optional) — `application` (default), `web` or `api`. Used for display.
- `request_id` (string, optional) — Optional. Repeating the same request with the same `request_id` returns the service it created instead of creating a second one. Reusing the ID with a different name or kind, or after that service was deleted, answers `409` with `request_id_conflict`.

```bash
curl -X POST https://api.fortly.com/projects/ormi-ada/services \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "API", "kind": "web"}'
```

Returns `201` with `{ "service": … }`. The new service's address is created from its name and does not change when the service is renamed.

## Deploy a service

Send a gzipped tar of the whole codebase, and name the service's folder in a header:

- `x-fortly-root-dir` (header, optional) — The service's folder inside the archive, such as `apps/api`. Omit it for the archive's top level.
- `x-fortly-install-dir` (header, optional) — Where to install dependencies from, when it is not the service's folder: the workspace root that contains it.

```bash
tar -czf ormi.tgz -C ~/code/ormi .
curl -X POST https://api.fortly.com/projects/ormi-ada/services/svc_df8e…/deploys \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/gzip" \
  -H "x-fortly-root-dir: apps/api" \
  --data-binary @ormi.tgz
```

To deploy the service's saved GitHub folder at its branch's latest commit, send JSON instead. Add `"sha"` with a full 40-character commit SHA to deploy that commit:

```bash
curl -X POST https://api.fortly.com/projects/ormi-ada/services/svc_df8e…/deploys \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"source": "github"}'
```

A release that is served as uploaded, such as a static site, is live when the request returns (`201`). A release that has to be built returns `202` while it builds; follow it with the releases endpoint. The service's own release is the only one that changes.

## Change a service

- `expected_revision` (integer, required) — The `revision` you last read. A stale value returns `409`.
- `name` (string, optional) — A new name.
- `database_access` (boolean, optional) — Give the service's runtime the project's PostgreSQL credentials. Owners and admins only.

```bash
curl -X PATCH https://api.fortly.com/projects/ormi-ada/services/svc_df8e… \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"expected_revision": 3, "database_access": true}'
```

## Variables

A service other than the application receives only its own variables. They use the same `name`, `value` and `scope` (`runtime`, `build` or `both`) as [project secrets](https://docs.fortly.com/api/secrets). Values are never returned.

```bash
curl -X POST https://api.fortly.com/projects/ormi-ada/services/svc_df8e…/variables \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "API_URL", "value": "https://api.example.com", "scope": "both"}'
```

The application's variables are the project's. Setting one here for the application returns `409` with `code: "project_variables"`.

## Delete a service

- `expected_revision` (integer, required) — The `revision` you last read.
- `confirm_name` (string, required) — The service's current name.

```bash
curl -X DELETE https://api.fortly.com/projects/ormi-ada/services/svc_df8e… \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"expected_revision": 3, "confirm_name": "API"}'
```

Returns `204`. Owners and admins only. The address stops answering immediately; the service's stored releases, variables and saved source are removed. The application can't be deleted this way (`409`, `code: "default_service"`); delete the project instead.

## Runtime logs and metrics

Operational reads are scoped to the project's current service identity. They check the caller's current project membership and reject a sibling's service ID under another project. The default service represents the main application's address. The project-level health/traffic/log routes remain the aggregate.

| Method | Route | Result |
| --- | --- | --- |
| GET | `/projects/{project}/services/{id}/health` | `service_id`, `health` and `since_restart`; health uses the same request/error/latency/CPU fields as project health |
| GET | `/projects/{project}/services/{id}/traffic?span=24h` | `span`, `step_s`, and ordered `points`; spans are `15m`, `1h`, `24h`, `7d` |
| GET | `/projects/{project}/services/{id}/routes` | `routes` and `limit`; route totals start at the last node restart |
| GET | `/projects/{project}/services/{id}/logs?lines=200` | Recent runtime `events`, `oldest`, `newest`, `more`; use `before` to page backwards |
| GET | `/projects/{project}/services/{id}/logs/stream` | Resumable server-sent events; `after` or `Last-Event-ID` resumes the service's own cursor |

Log events carry `seq`, `ts`, `source`, `stream` and `text`. Newly captured runtime events also include `context` with the stored `service_id` (or null for a legacy main release) and `deployment_id`; the console links this identity back to the build. The event format remains backward-compatible, in the same shape as [project log guide](https://docs.fortly.com/deploy/logs#logs). `lines` is clamped to 1–1000. A service stream checks token validity, current membership and service existence again every ten seconds. Deleted services refuse future reads and tail callbacks. Output is bounded recent runtime output, not unlimited log retention; per-release build output stays at the deployment's `/logs` endpoint above.

Request/error/CPU summary totals cover the last 24 hours; latency percentiles and route statistics cover the period since the node restarted. Service measurements begin with the monitoring rollout. Earlier aggregate history is retained in project-level views. Per-address counters are separate dimensions of existing requests; account usage continues to sum the project's aggregate once. Seven-day charts combine recorded closed hours with the current hour. Scoped runtime copies pass through the project's existing secret redaction before storage and streaming.

## Activate a retained service release

`POST /projects/{name}/services/{service}/deployments/{deployment}/activate` restores a ready or previously published release for that exact service. It requires current project membership, an active service and retained artifacts that pass verification. A foreign service/release pair returns `404`; an unavailable or invalid artifact returns `410`. Only this service's pointer, route and cached responses change. The response is `{ "deploy": ... }`.

This operation does not rebuild code, rerun migrations or restore database contents. The main application's existing `/deploys/{id}/activate` endpoint uses the same verification path.

Build-output responses can include `deployment_progress` with `state`, `phase`, `failed_phase`, `migration_tool`, `migrations_completed` and `updated_at`. This progress is separate from the builder's `phase`. If the builder is temporarily unavailable, an admitted deployment can still return authoritative progress with `output_status: "unavailable"`; `"expired"` means no retained builder output is available. Clients must continue following deployment state after compilation finishes.
