Create Endpoint

POST /v1/endpoints

Provision a new endpoint — the routing entry that connects a public URL to an agent. The endpoint persists across agent reconnects; tunnels are the live connections that come and go on top of it.

Endpoints are protocol-agnostic — one endpoint serves HTTP, TCP, UDP and QUIC at once. The protocol is chosen per tunnel by the agent (ngris http|tcp|udp), not at endpoint create, so there is no protocol field. Endpoints are also region-less — region is a property of the serving tunnel/agent (GeoDNS steers public traffic to the nearest region), so there is no region field; the plan's region:<slug> entitlements are enforced when a tunnel connects, not at create. Before creating, the server checks that the user has remaining endpoint quota on their plan and (for custom subdomains and root @ endpoints) that the user owns the target domain. Per-protocol plan entitlements are enforced when a tunnel of that protocol connects.

To serve an application instead of a tunnel, send serving_mode: "agentless" and the app's application_uuid. An app can have several endpoints (for example example.com and www.example.com): send the same application_uuid again for each. Every endpoint serves the app's production deploy with its own settings — a new endpoint does not get the sign-in of the app's other endpoints, so set it with PUT /v1/endpoints/{uuid}/settings (or one mode on many at once with POST /v1/endpoints/bulk-settings). The app's first endpoint becomes its primary. Every endpoint, app or tunnel, counts against the same endpoint quota.

Only a function gets an endpoint when it is created (and Quick Deploy makes one for a static app), so this call is how a static, backend, full-stack or WordPress app gets its URL. Adding an endpoint to a WordPress app restarts the site so it picks up its new address: about a minute of downtime.

Request body

subdomain string
Optional. Only honoured when is_custom: true and you own the target domain — otherwise the platform auto-generates a memorable word-word-word identifier. Pass "@" to claim the root of the domain (admin or strict domain owner only).
is_custom boolean
Set to true to use the supplied subdomain. Requires the custom-subdomains entitlement on your plan and ownership of the target domain. Silently downgraded to false if either prerequisite isn't met.
domain_uuid string (uuid)
The uuid of a domain you can use — one registered to your account or a platform domain (/domains). Omit to use the plan's configured default system domain (e.g. ngris.io).
serving_mode string
Optional. Omit for a tunnel endpoint (served by an agent). agentless (or managed) serves the application named by application_uuid; the platform derives how from the app's type (static files from the edge, or the app's backend).
application_uuid string (uuid)
Required with serving_mode agentless or managed: the app in your active account to serve. The app may already have other endpoints. Requires application hosting on your plan.

Deprecated: domain_id (integer) is still accepted until the sunset. Send domain_uuid; sending both for different objects is a 400 id_uuid_mismatch.

Response (201 Created)

uuid string (uuid)
The endpoint's public id, used in every other endpoint URL (/endpoints/{uuid}).
user_uuid string (uuid)?
The user who created the endpoint. Omitted when the endpoint has no creating user.
subdomain string
Final subdomain (auto-generated if you didn't pass one).
hostname string
The endpoint's public FQDN (e.g. myapp.example.com). Share this with the agent and clients.
public_url string
Same value as hostname — the bare FQDN with no scheme, because the endpoint may serve HTTP/TCP/UDP/QUIC. For HTTP/unified endpoints, build a browser URL as https://<public_url>.
serving_mode string
agent (a tunnel — the default), agentless or managed (an application endpoint).
application_uuid string (uuid)?
The application an application endpoint serves. Omitted for agent endpoints.
protocol string
Always unified — endpoints are protocol-agnostic.
is_custom boolean
Whether the final subdomain is user-chosen. May be false even if you requested true (entitlement or ownership check failed).
domain_uuid string (uuid)?
The uuid of the domain the endpoint is anchored to. Omitted when the endpoint has no domain row.
status string
inactive — the endpoint exists and turns active once an agent (or, for an application endpoint, a live deploy) serves it — or pending_dns when the platform still has to create the endpoint's DNS record on your domain (it then moves to inactive, or dns_failed with dns_last_error set).
mtls_mode string
mTLS state as stored (disabled, optional, required): disabled for a new endpoint; configure via PUT /v1/endpoints/{uuid}/mtls.
capture_enabled boolean
Whether requests are recorded in the traffic logs, as stored: true for a new endpoint. Change it via PUT /v1/endpoints/{uuid}/settings/capture.
capture_level string?
The advisory capture hint (full | metadata | off), as stored. Omitted when unset, as on a new endpoint.
ephemeral boolean
Always false for an endpoint created here.
dns_attempts int
DNS provisioning attempts so far — 0 at creation.
created_at, updated_at datetime
RFC 3339 UTC timestamps.

Deprecated: until the sunset this response also carries the legacy integer id and *_id fields. Don't use them; read uuid and the *_uuid fields.

Errors

400 Bad Request
Invalid JSON, unknown fields (strict decoding; stale protocol/region fields are accepted-but-ignored), invalid subdomain format, a malformed domain_uuid, domain_id and domain_uuid naming different domains (id_uuid_mismatch), or an application endpoint without application_uuid (application_uuid is required for an application endpoint).
401 Unauthorized
Missing or invalid API key.
403 Forbidden
Endpoint quota reached: {"error":"endpoint limit reached (N)"}, where N is your plan's endpoint limit. Every endpoint counts, app and tunnel endpoints alike (ephemeral endpoints have their own quota); remove one or upgrade your plan. Also: root @ endpoint without strict ownership, organization endpoint limit hit, or an application endpoint on a plan without application hosting (Application hosting is not available on your plan). (Per-protocol and per-region plan entitlements are enforced when a tunnel connects, not at endpoint create.)
404 Not Found
domain_uuid names no domain you can use (domain not found — an unknown domain and another account's domain read the same), or application_uuid no application in your active account (application not found).
409 Conflict
The application is paused over your plan's limits (quota_paused): no endpoint can be added to it until the account has room again.
500
Database / plan-lookup error. The transaction is rolled back; safe to retry.
Request
curl -X POST "https://api.ngris.com/v1/endpoints" \ -H "X-API-KEY: <your_api_key>" \ -H "Content-Type: application/json" \ -d '{ "subdomain": "myapp", "is_custom": true, "domain_uuid": "019bd08f-98db-71b2-8e54-f518d319f111" }'
Python
from ngris import Ngris client = Ngris(api_key="<your_api_key>") ep = client.endpoints.create({ "subdomain": "myapp", "is_custom": True, "domain_uuid": "019bd08f-98db-71b2-8e54-f518d319f111", }) print(ep.hostname) # myapp.example.com (public_url is the same bare FQDN, no scheme)
Request — another endpoint for an application
curl -X POST "https://api.ngris.com/v1/endpoints" \ -H "X-API-KEY: <your_api_key>" \ -H "Content-Type: application/json" \ -d '{ "serving_mode": "agentless", "application_uuid": "019be838-d261-7d9b-9499-71995a3a91c6", "subdomain": "www", "is_custom": true, "domain_uuid": "019bd08f-98db-71b2-8e54-f518d319f111" }'
Response — 201 Created
{ "uuid": "019bb1e4-e785-77d2-9966-c3cff31d0a0e", "user_uuid": "019a52c2-ca1e-78ba-ab5c-50362cc29134", "subdomain": "myapp", "hostname": "myapp.example.com", "public_url": "myapp.example.com", "protocol": "unified", "is_custom": true, "domain_uuid": "019bd08f-98db-71b2-8e54-f518d319f111", "serving_mode": "agent", "status": "inactive", "mtls_mode": "disabled", "capture_enabled": true, "ephemeral": false, "dns_attempts": 0, "created_at": "2026-04-29T12:34:56Z", "updated_at": "2026-04-29T12:34:56Z" }
Response — 403 Forbidden (endpoint quota)
{ "error": "endpoint limit reached (N)" }
Was this helpful?
On this page
Create Endpoint
Was this helpful?