Endpoint Reference
All endpoints are relative to https://surge.surge.sh and require token authentication unless noted. Errors mostly use the { messages, details } shape described in the overview. Calling from JavaScript? The SDK Method Reference maps each endpoint to its method.
Projects
| Method & path |
Description |
PUT /:domain |
Deploy a gzipped tarball to the domain. Streaming—see Deploys. |
DELETE /:domain |
Tear the project down. 200 { "msg": "project removed" }. |
GET /list |
All projects on the account. Array of { domain, rev, timeAgoInWords, … }. (planName is a legacy per-project label; read the plan from GET /account.) |
GET /:domain/metadata.json |
Metadata for the live revision: revision id, deploy message, publisher email, file counts, sizes, and the project's active config. GET /:domain/:rev/metadata.json targets a specific revision. |
GET /:domain/manifest.json |
The file manifest—a map of path to { size, md5 }. Also available per revision. |
DELETE /:domain/cache |
Purge the project from every edge node's cache. 200 { "confirmed": [...], "unconfirmed": [...] }. |
POST /:domain/carry |
Upload dedupe: send the project's file list and checksums, get back the files the live revision lacks and a carry-ticket for the deploy. Optional—see Deploys. |
GET /:domain/status |
Where the domain stands: { status, action, https, reason, records }. status is waiting on dns, securing, ready (a new account's surge.sh domain awaiting email verification), or live. |
Revisions
Revision ids are millisecond timestamps (e.g. 1719852000000). Each revision has its own preview URL, and the last 100 per domain are kept.
| Method & path |
Description |
GET /:domain/list |
All revisions of the project: { rev, friendlySize, timeAgoInWords, publicFileCount, … }. 404 if none. |
POST /:domain/rollback |
Point the domain at the previous revision. 201 { revision, former, instances } when it moved, 200 when it was already there. |
POST /:domain/rollfore |
Point the domain at the next revision. Same responses. |
PUT /:domain/rev |
Cut over to the latest revision; PUT /:domain/rev/:rev to a specific one. |
DELETE /:domain/rev/:rev |
Discard a revision from the system. |
Certificates
| Method & path |
Description |
GET /:domain/certs |
Certificate information for the domain. |
PUT /:domain/encrypt |
Provision a managed certificate. Streaming NDJSON—see Deploys. |
PUT /:domain/ssl |
Upload a custom PEM bundle. Content-Type: application/x-pem-file. Pro only. Always 200; the outcome is the final msg line's status. |
DNS
DNS endpoints apply to apex domains delegated to Surge's name servers; on other domains they respond 405. Records take the form { type, name, value, priority }.
| Method & path |
Description |
GET /:domain/dns |
The custom records you've added. |
POST /:domain/dns |
Add a record. 400 on validation failure. |
DELETE /:domain/dns/:id |
Remove a record. |
GET /:domain/zone |
The full zone, including the records Surge manages automatically. |
POST /:domain/zone |
Add a zone record. |
DELETE /:domain/zone/:id |
Remove a zone record. |
Collaborators
| Method & path |
Description |
POST /:domain/collaborators |
Closed: orgs share domains now. Answers 200 with each email "status": "not sent" and what to do instead; nothing is sent. |
DELETE /:domain/collaborators |
Revoke collaborators invited before orgs. Body { "emails": [...] }. 202 { "successes": [...], "fails": [...] }. |
Settings & analytics
| Method & path |
Description |
PUT /:domain/settings |
Update the domain's settings. Body is a map of setting → value—force (http/https), redirect (a domain), cors (a domain or *), hsts (seconds), private (true/false), any of them null to clear; the response echoes the resulting configuration. |
GET /:domain/analytics |
Aggregated traffic analytics for the past 14 days: visits, uniques, connections, bandwidth, status codes, encryption breakdown, sources, and per-file load. Cached for 15 minutes. |
GET /:domain/usage |
Bandwidth usage—same data source as analytics. |
GET /:domain/audit |
The project's state on every edge node, including a live TLS certificate check per node. |
Account, tokens & billing
| Method & path |
Description |
POST /token |
Mint an API token. Optional body { msg, scope }. 201 { email, token, id, msg, scope, created_at }. See Authentication. |
GET /tokens |
The account's tokens, by id. |
DELETE /tokens/:id |
Expire a token. 204. |
PUT /tokens/:id |
Hand a token to an org the account is in: { org }. Its value doesn't change. |
POST /token/reset/:email |
Send password reset instructions. No auth. Always 201. |
POST /verification |
Send an email verification link. |
GET /account |
The account object, with plan and card. |
DELETE /account |
Permanently delete the account. 409 while projects still exist or the account is an org's only admin. |
GET /plans |
Available plans, the current plan, and card status. |
GET /subscription |
The account's current subscription. |
PUT /plan |
Subscribe to or change a plan. Body { plan, token, timestamp }. |
PUT /card |
Update the payment card. Body { token } (a Stripe source token). 201 on success. |
POST /upgrade/link |
Mint a browser checkout URL, valid 48 hours. 201 { url }. |
POST /billing/link |
Mint a browser billing portal URL (card, invoices, cancellation), valid 48 hours. 201 { url }. |
Orgs
:org is an org's slug or id. Non-members get 404, the same as for an org that doesn't exist. See surge orgs.
| Method & path |
Description |
GET /orgs |
The orgs the account belongs to, with its role in each. |
POST /orgs |
Create an org: { slug, name }. The caller becomes its admin. Needs a verified email. |
GET /orgs/:org |
An org and its members. Members only. |
PUT /orgs/:org |
Change slug and/or name. Any member. |
DELETE /orgs/:org |
Remove the org. Admins only; refused while it holds domains (transfer or tear them down first). |
GET /orgs/:org/members |
Members and pending invites. A pending invite has no email: it's listed by hint, its code's first word. |
GET /orgs/:org/members/:email |
One member. |
POST /orgs/:org/invites |
Mint an invite: 201 { code, hint, role, expires_at }. Nothing is emailed. Single-use, valid 7 days, up to 10 waiting per org. Optional { role }: member (the default) or admin. Any member; inviting an admin, admins only. |
PUT /orgs/:org/members/:email |
Set { role } to admin or member. Any change to or from admin is for admins. |
DELETE /orgs/:org/members/:email |
Remove a member. Any member; removing an admin, admins only; anyone, for themselves. |
POST /orgs/:org/join |
Join with { code }, as the signed-in account. The same as POST /claim with an invite. |
GET /orgs/:org/tokens |
The org's tokens by id: scope, msg, created_by, uses, last used. Never the secret. Any member. |
POST /orgs/:org/tokens |
Add an org token: { msg, scope }. 201 { org, token, id, msg, scope, created_by, created_at }; the token is shown once. Any verified member, signed in as themselves; up to 10 per org. |
DELETE /orgs/:org/tokens/:id |
Expire an org token. 204. Any member. |
An org token acts as the org. It publishes the org's domains (new ones become the org's), rolls back, cuts over, tears down, and reads /list, revisions, status and certs. Every other endpoint answers 403. It outlives whoever added it.
Transfers
A domain moves into an org you're in, out of one to your own account, or—by a transfer code—to anyone. Serving is unchanged. See Moving a domain.
| Method & path |
Description |
GET /:domain/owner |
Who owns the domain, for anyone who holds it: { owner: { account } }, { owner: { org: { slug, name } } }, or "someone else". |
PUT /:domain/owner |
Move it: { to } is an org slug you're a member of, or your own email. The owner account or any member of the owning org. Answers { domain, owner, from, collaborators }. |
POST /:domain/owner/code |
Mint a transfer code: 201 { code, expires_at }, valid 7 days. A new code cancels the last. |
POST /claim |
Claim with { code }. A transfer code: the domain becomes the signed-in account's ({ domain, owner, orgs }), once, and only while the domain still has the owner it was minted under. An invite: the account joins the org ({ org: { slug, name }, role, already }). With { code, check: true } it does nothing and describes the code instead: { check, kind: "transfer", domain, from, by, expires_at, yours } or { check, kind: "invite", org, role, by, expires_at, member }. After 10 codes that didn't work in an hour, per account or IP, answers 429. |
POST /scrap |
Cancel { code }: an invite, by any member of its org ({ scrapped: "invite", org }), or a transfer code, by whoever may mint one for the domain ({ scrapped: "transfer", domain }). |
Tokens scoped to a domain can read its owner but can't move it or mint a code.
Sign-in sessions
These back surge login --browser and surge mfa; most tools never need them.
| Method & path |
Description |
POST /login/session |
Start a browser sign-in. No auth. { uuid, url, code, poll, interval, expires_in }. |
POST /login/session/:uuid/poll |
Poll it with { poll }: status is pending, approved (with email and token, once), or gone. |
POST /mfa/session |
Open the two-factor setup page for the account. { url }. |
Feeds & platform stats
| Method & path |
Description |
GET /stats |
Public, unauthenticated platform statistics—deploy counts, bytes published, project totals. CORS-enabled. |
GET /pulse |
Public server-sent event stream of completed publishes, anonymized. CORS-enabled. |
GET /activity |
Server-sent event stream of every change on the account's domains. Authenticated. |
This page as markdown · every page in llms-full.txt