Services
When you deploy backend code to Forte, it becomes a Service: a containerized app that Forte hosts, scales, and monitors.
How services work
To call a Service, send an HTTP request to its Forte endpoint. The Forte API gateway authenticates the User, routes the request to your Service over a private network, and records request logs and metrics.
Forte sits in front of every Service, so authentication, rate limiting, and DDoS protection happen before a request reaches your code:
Supported languages and frameworks
Forte detects common backend languages and frameworks from your repository and builds a container image. You don't need to provide a Dockerfile for supported stacks.
If your repository already includes a Dockerfile, Forte uses it as-is, so any language or framework that runs in a container is supported. Either way, Forte discovers the port your app listens on and runs it behind the gateway with autoscaling, TLS, and observability.
Service endpoint
Every service deployed on Forte gets a unique HTTPS endpoint on the tryforte.dev domain. This is your service's invocation URL — the address your frontend, mobile app, or any client uses to send requests to your backend.
Forte creates this permanent endpoint with the service. It stays the same across deployments, so you can use it as your backend URL.
Custom domains
Custom domains are available. Add one from the service's page in the Forte Console, then point your DNS at the domains.tryforte.dev endpoint shown there (a CNAME). Forte provisions and renews the TLS certificate for you. Your generated tryforte.dev endpoint keeps working alongside your custom domain.
Built-in tools
Forte includes tools for hosting and maintaining your services:
- Request debugging: Forte records each request and links it to related app logs.
- Automatic builds: Forte detects your code's language and framework. It uses your Dockerfile or generates one when needed.
- Integrated monitoring: Forte provides a dashboard for your performance metrics and sends alerts when it detects errors or performance regressions.
Environment variables and service tokens
Every service deployed on Forte automatically receives three environment variables at runtime:
| Variable | Description |
|---|---|
FORTE_PROJECT_ID | The ID of the project this service belongs to |
FORTE_SERVICE_ID | The ID of this service |
FORTE_API_TOKEN | A Bearer token scoped to this service's project |
Your service can use FORTE_API_TOKEN to call Forte's server-side API — for example, to manage users, read project data, or interact with other resources within the same project. Include it as a Bearer token in your requests:
Server-side (API key):
curl -H "Authorization: Bearer $FORTE_API_TOKEN" \
https://api.tryforte.dev/api/v1/projects/$FORTE_PROJECT_ID/usersIf you use the Forte runtime SDKs for TypeScript, Java, or Python, credentials are loaded automatically from these environment variables — no manual configuration needed.
The service token can only access resources within its own project. It cannot access other projects or perform account-level operations.
FORTE_API_TOKEN is a server-side secret. Never expose it to a browser or commit it to your repository. To let your signed-in users call Forte directly from your frontend, use Forte's client-side API instead — it uses the user's session cookie, not the project API key.
The FORTE_ prefix is reserved — you cannot create custom environment variables that start with FORTE_.
Build triggers
Each service deploys from a GitHub repository. Choose how new deployments are triggered:
- On every push to branch — every commit to the configured branch builds and deploys automatically. Best for staging and continuous-deployment workflows.
- On GitHub release published — only deploys when a new GitHub release is published. Best for production services that should roll forward on tagged releases.
You can also deploy any commit manually. See Deploying services for instructions.
Scaling and background work
Forte automatically scales Service compute down when your code isn't actively handling web requests.
Because of this, background work scheduled inside your service is not reliable. Timers, cron loops, polling threads, and in-process schedulers (for example a setInterval or a scheduler library running in your app) can be paused or stopped when your service scales down, and may never fire while your service is idle. Don't rely on your service staying warm to run work on a schedule.
To run work on a schedule or asynchronously, use actions instead — Forte calls a path on your service on a cron schedule or at a one-time future moment, so the work runs even when your service isn't handling traffic. See Create your first action to set one up.
Pausing a service
You can pause a service from its console page. Pausing stops all the service's provisioned compute — and with it the service's compute billing — until you resume it.
While a service is paused:
- Requests are rejected. The service's endpoint returns
503with error codeSERVICE_PAUSED. Requests are rejected before they reach your app, so they don't appear in request logs or metrics. - Compute billing stops. You are not billed the base or active compute rates while the service is paused. See Pricing.
- GitHub-triggered builds are skipped. A push (or published release) to the service's repository does not build, deploy, or resume it. The build is recorded in the deployment history and immediately cancelled with a "Build skipped" message, so you can see which commits were skipped — and skipped builds are free. To deploy the latest code, resume the service and retry the build.
- Manually triggered deployments still run. A deployment you start yourself — retrying a build from the console or saving a configuration change that triggers a rebuild — builds and deploys normally, and automatically resumes the service.
The service's endpoint doesn't change while paused — resuming brings it back online at the same URL. You can't pause a service while a build is in progress.
Pause and resume are also available on the server-side API as forte.projects.pauseService and forte.projects.resumeService.
Authentication path exclusions
By default, every request to your service is authenticated — Forte verifies the user's identity before forwarding the request to your app. If some routes need to be publicly accessible (health checks, webhooks, public APIs), configure authentication path exclusions to bypass user authentication for specific paths.
Pattern syntax
Path exclusions use Ant-style patterns, supporting three wildcards:
| Token | Meaning | Example |
|---|---|---|
? | Matches exactly one character | /api/v?/status matches /api/v1/status |
* | Matches zero or more characters within a single path segment | /api/*/health matches /api/orders/health |
** | Matches zero or more path segments | /api/public/** matches /api/public/foo/bar/baz |
Common examples
| Pattern | Matches | Does not match |
|---|---|---|
/health | /health | /health/deep |
/api/public/** | /api/public, /api/public/users, /api/public/a/b/c | /api/private |
/webhooks/*/callback | /webhooks/stripe/callback | /webhooks/stripe/callback/retry |
/**/*.css | /styles/main.css, /assets/fonts/icon.css | /styles/main.js |
Testing your patterns
When editing a service in the console, use the built-in path tester to verify your patterns before saving. Enter a request path and click Test to see whether it would be excluded from authentication and which pattern matched.
Excluding paths from authentication means those requests aren't tied to a user identity. This limits Forte's per-user observability, rate limiting, and abuse protection on those routes. Keep exclusions few, and use them only for genuinely public endpoints.
CORS preflight
Browser CORS preflight requests (OPTIONS) are automatically forwarded to your service without Forte authentication. This lets your service own its CORS policy — set the Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers response headers from your code, and Forte returns them to the browser unchanged.
This applies only to OPTIONS. Every other method — including HEAD — is still authenticated like a normal request. (Browsers send credentials on HEAD the same way they do on GET, so treating HEAD as auth-exempt would let unauthenticated callers probe response headers on protected paths.)
You do not need to add OPTIONS paths to your authentication path exclusions — preflight is exempt platform-wide.
First-party auth routes (/_forte)
Every service reserves a /_forte path prefix that forwards to Forte's user-authentication routes. It exists so your frontend can set the Forte-User-Session-Token cookie on your own domain instead of Forte's — a first-party cookie that browsers won't block. Point the SDK's baseUrl at your service's /_forte prefix when you need cookies to match your domain:
import { ForteClient } from "@forteplatforms/sdk";
// forte.users.* calls travel through /_forte on your own domain,
// so the session cookie is scoped to your site.
const forte = new ForteClient({ baseUrl: "https://yourapp.com/_forte" });Only end-user (forte.users.*) routes are reachable through /_forte, and requests through it are handled by Forte before they reach your service (so they don't appear in your request logs or metrics). See SDKs → First-party cookies via /_forte for the full details.
Response streaming
The gateway streams your service's response back to the caller as your service produces it, instead of waiting for the full body. If your service writes and flushes output incrementally, each chunk reaches the client the moment you flush it.
This means Server-Sent Events (SSE) and chunked responses work through the gateway with no configuration. Token-by-token output from a model endpoint is forwarded as your service generates it, and large file downloads stream straight through without the gateway holding them in full.
Streaming applies to the response your service returns. Request bodies are received in full before the request is forwarded to your service.
Every request to a service must complete within 120 seconds, measured from when the gateway receives the request to when your service finishes writing the response. Streaming suits responses that finish inside that window, such as progressive rendering or model output — not connections held open indefinitely, like an event stream that never closes. If your service hasn't started responding when the limit is reached, the gateway returns a 504.