Services API
List, create, deploy and delete a project's services, and manage each one's variables and database access.
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.
/ projects/ {name}/ servicesThe project's services/ projects/ {name}/ servicesCreate a service/ projects/ {name}/ services/ {service}One service/ projects/ {name}/ services/ {service}Rename it or change its database access/ projects/ {name}/ services/ {service}Delete it/ projects/ {name}/ services/ {service}/ deploysDeploy an archive or its saved GitHub folder/ projects/ {name}/ services/ {service}/ deploymentsIts releases/ projects/ {name}/ services/ {service}/ deployments/ {id}/ logsOne release's build output/ projects/ {name}/ services/ {service}/ variablesIts variables — names only/ projects/ {name}/ services/ {service}/ variablesSet a variable/ projects/ {name}/ services/ {service}/ variables/ {key}Remove a variableList services
curl https://api.fortly.com/projects/ormi-ada/services \
-H "Authorization: Bearer $FORTLY_TOKEN"{
"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
namestringRequiredkindstringapplication (default), web or api. Used for display.request_idstringrequest_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.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-dirheaderapps/api. Omit it for the archive's top level.x-fortly-install-dirheadertar -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.tgzTo 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:
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_revisionintegerRequiredrevision you last read. A stale value returns 409.namestringdatabase_accessbooleancurl -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.
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_revisionintegerRequiredrevision you last read.confirm_namestringRequiredcurl -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. 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.