HTTP API

Base URL https://api.anchoredip.com/api/. Everything the dashboard does, it does through this — there is no private API behind it.

Authentication

There is none, in the usual sense. A lease has an access token, returned once when you order, and it appears in the URL of every call about that lease. No account, no OAuth, no key rotation.

That token is the only credential on the lease. Treat it like a password: anyone holding it can create tunnels and egress as your address. If you lose it, we email it back to your billing address; if it leaks, revoke the peers and tell us.

Plans

GET /api/plans/

response

[
  {
    "slug": "solo",
    "name": "Solo",
    "monthly_price_usd": "19.00",
    "term_price_usd": "19.00",
    "term_days": 30,
    "trial_days": 0,
    "included_ipv4": 1,
    "included_ipv6": 1,
    "included_tunnels": 2,
    "requires_verification": false,
    "payment_methods": [{ "value": "card", "name": "Card", "detail": "…" }]
  }
]

Read prices from here rather than hardcoding them. The headline monthly_price_usd is per month whatever the term; term_price_usd is what is actually charged. On an annual plan they differ by a factor of twelve.

Order a lease

POST /api/leases/
Content-Type: application/json

{
  "plan": "trial",
  "organization_name": "Example GmbH",
  "country": "DE",
  "billing_email": "ops@example.com"
}

response — free or trial plan

{ "access_token": "884c53fe-…", "status": "active" }

response — paid plan

{
  "access_token": "69c81e11-…",
  "status": "pending",
  "amount_usd": "19.00",
  "payment_reference": "AIP-00021",
  "payment_methods": [ … ]
}

Limited to five orders an hour from one address. A trial is one per billing email; a second is refused with a 400 that names the paid plans, which need no approval.

Read a lease

GET /api/leases/{access_token}/

Returns status, the addresses held, every tunnel with its last handshake, what is owed, and the full billing history with a link to each invoice. This is what the dashboard renders.

Create a tunnel

POST /api/leases/{access_token}/tunnels/

{
  "transport": "wireguard",     // or "amneziawg"
  "routing_mode": "source",     // or "full"
  "label": "eu-worker-1"
}

response

{
  "tunnel_id": 42,
  "filename": "anchoredip-42.conf",
  "config": "[Interface]\nPrivateKey = …",
  "warning": "This configuration contains a private key and is returned once."
}
The private key exists only inside this response. Nothing stores it, us included. Write it to a file in the same step that creates it, or you will be creating another tunnel.

Pick source for a Linux host with wg-quick and full for anything else — a source-routed config cannot work in a graphical client. See routing modes.

Revoke a tunnel

DELETE /api/leases/{access_token}/tunnels/{tunnel_id}

Frees the slot immediately. Meant to be callable from a shutdown hook: a container that revokes its own peer on exit will not exhaust your allowance with ghosts.

Reverse DNS

GET  /api/leases/{access_token}/rdns/
POST /api/leases/{access_token}/rdns/

{ "address": "194.0.108.64", "hostname": "mail.example.com" }

An empty hostname removes the record. Live within about a second — it is a row in our nameserver, not a ticket to somebody else.

Payment and renewal

POST /api/leases/{access_token}/renew/         { "plan": "solo" }
POST /api/leases/{access_token}/pay/           { "method": "card" }
POST /api/leases/{access_token}/pay/           { "method": "crypto", "txid": "…" }
GET  /api/leases/{access_token}/invoice/?number=AIP-2026-0003

renew without a plan renews the current one; a trial has none to renew into, so it answers 400 with the plans it can move to. Naming a plan converts the lease and keeps the addresses, which is the entire reason the trial runs on real space.

Recover access

POST /api/auth/link/     { "email": "ops@example.com" }
POST /api/auth/redeem/   { "key": "…from the email…" }

The first emails a one-time link, valid thirty minutes; the second exchanges it for every lease billed to that address. An address we do not bill gets exactly the same answer as one we do.

Errors

Ordinary HTTP. 400 for a request that cannot be satisfied as written, 404 for an access token we do not know, 409 when the request is well formed but the world is not ready — a crypto payment the chain has not confirmed yet, or a renewal on a lease that has ended — 429 when throttled. The body carries detail with a sentence you can show a person.