Docs

Services API

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

Updated November 1, 2026
On this page
  1. List services
  2. Create a service
  3. Deploy a service
  4. Change a service
  5. Variables
  6. Delete a service
  7. Runtime logs and metrics
  8. Activate a retained service release

A service is a separately deployed part of a project, with its own address, releases and variables. See Services for how they work. {name} is the project's name and {service} is a service id.

GET/projects/{name}/servicesThe project's services
POST/projects/{name}/servicesCreate 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}/deploysDeploy an archive or its saved GitHub folder
GET/projects/{name}/services/{service}/deploymentsIts releases
GET/projects/{name}/services/{service}/deployments/{id}/logsOne release's build output
GET/projects/{name}/services/{service}/variablesIts variables — names only
POST/projects/{name}/services/{service}/variablesSet a variable
DELETE/projects/{name}/services/{service}/variables/{key}Remove a variable

List services

Terminal
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 and 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

namestringRequired
1–48 characters, unique within the project.
kindstring
application (default), web or api. Used for display.
request_idstring
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.
Terminal
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-dirheader
The service's folder inside the archive, such as apps/api. Omit it for the archive's top level.
x-fortly-install-dirheader
Where to install dependencies from, when it is not the service's folder: the workspace root that contains it.
Terminal
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:

Terminal
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_revisionintegerRequired
The revision you last read. A stale value returns 409.
namestring
A new name.
database_accessboolean
Give the service's runtime the project's PostgreSQL credentials. Owners and admins only.
Terminal
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. Values are never returned.

Terminal
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_revisionintegerRequired
The revision you last read.
confirm_namestringRequired
The service's current name.
Terminal
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.

MethodRouteResult
GET/projects/{project}/services/{id}/healthservice_id, health and since_restart; health uses the same request/error/latency/CPU fields as project health
GET/projects/{project}/services/{id}/traffic?span=24hspan, step_s, and ordered points; spans are 15m, 1h, 24h, 7d
GET/projects/{project}/services/{id}/routesroutes and limit; route totals start at the last node restart
GET/projects/{project}/services/{id}/logs?lines=200Recent runtime events, oldest, newest, more; use before to page backwards
GET/projects/{project}/services/{id}/logs/streamResumable 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. 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.

Something missing or unclear? Tell us and we will fix the page.© 2026 Fortly