EndpointOS

EndpointOS docs

How to package, operate, and ship an API as a product. Read top-to-bottom for the full picture, or jump to a section.

5-minute start

Make your first authenticated request in any stack. Snippets below switch language; replace you, your-project, and your-resource with your own.

Full walk-through
curl -X POST https://endpointos.dev/api/public/you/your-project/your-resource \
  -H "Authorization: Bearer sapi_live_..." \
  -H "Content-Type: application/json" \
  -d '{"title":"Add dark mode"}'

Getting started

From signup to first 200 OK in roughly five minutes. The five steps below are the canonical path; every other surface in the product builds on top of these primitives.

1. Create an account

Sign up at /signup. You'll pick a username (your public namespace; it appears in generated docs URLs like endpointos.dev/{username}/docs/{project}), confirm your email, and land in your personal workspace.

2. Create a project

From the dashboard, click New project. A project bundles resources, API keys, consumers, logs, usage limits, contracts, and webhooks. One project per API surface is the typical pattern (e.g. "feature-requests-api", "leads-api"). Each project gets its own slug and base URL.

3. Define your first resource

A resource is a named data shape. Add fields with types (string, number, boolean, enum, datetime) and rules (required, default, enum values). EndpointOS generates a CRUD API surface for the resource the moment you save:

GET    /api/public/{username}/{project}/{resource}
POST   /api/public/{username}/{project}/{resource}
GET    /api/public/{username}/{project}/{resource}/{id}
PATCH  /api/public/{username}/{project}/{resource}/{id}
DELETE /api/public/{username}/{project}/{resource}/{id}

4. Issue an API key

Open the API keys tab on your project and click New key. Give it a name (e.g. "Production", "Staging", "Partner X"). The plaintext key is shown exactly once. Copy it now. Subsequent visits show only the prefix and last 4 chars.

5. Make a request

Send the key as a bearer token or via the x-api-key header:

curl -X POST https://endpointos.dev/api/public/you/feature-requests-api/feature-requests \
  -H "Authorization: Bearer sapi_live_..." \
  -H "Content-Type: application/json" \
  -d '{"title":"Add dark mode","submitterEmail":"ada@example.com"}'

Hit the Logs tab and you'll see the request, the status, the latency, and the request body (truncated). That's the loop: define → call → observe.

A generated public API reference page, auto-built from a project's schema. Sidebar with sections, endpoint cards, curl examples, OpenAPI download.
What integrators get the moment you publish: a hosted reference page at /{username}/docs/{project} with OpenAPI download, code samples, and per-endpoint detail.

Projects & resources

A project is the unit of API ownership. It groups everything one API product needs: resources (data shapes), records (the actual data), API keys (auth), consumers (named end-customers), logs, usage, contracts (versions of the API surface), and webhooks.

A resource is one data shape inside a project. Each resource has fields with types and validation rules. EndpointOS stores records for that resource as JSON, and every record gets a stable id on create.

Field types

  • string: text. Validated by length when bounds are set.
  • number: JSON number. Validated by min/max when bounds are set.
  • boolean: true/false.
  • enum: one of a fixed list of strings.
  • datetime: ISO-8601 UTC timestamp.
  • json: arbitrary nested JSON object. Use sparingly; loses validation.

Field rules

  • required: POST/PATCH that omits the field returns 422.
  • default: value applied on POST when the field is omitted.
  • Enum values: restrict an enum field to a closed set of strings.
  • Min/max: bounds on string length and number value.

Data sources

  • EndpointOS-managed: records live in our database as JSON. Zero setup. Default for new resources.
  • Supabase: records live in a table in your own Supabase project. Paste the project URL, a service-role key (encrypted at rest), and the table name. EndpointOS handles auth, validation, monthly caps, and logs every request; record CRUD is proxied to your Supabase via PostgREST.
  • External Postgres: any HTTPS-reachable Postgres (Neon, RDS, self-hosted, etc.). Connection string is encrypted at rest. Parameterised queries only; SSRF guard re-checks the host on every connect.
  • BigQuery (read-only): expose a filtered, paginated view of a warehouse table. Service-account JSON auth (bigquery.readonly scope). Parameterised SQL via the jobs.query REST endpoint.
  • Firestore (full CRUD): point a resource at a Firestore collection over the REST API. Typed-value format translated to plain JSON at the boundary so your clients never see Firestore's wire shape.
  • External REST: any HTTPS endpoint. EndpointOS becomes the stable, branded, observable front door; we proxy CRUD upstream. Optional auth header, SSRF guard on every call.

What you can't do (yet)

  • Relationships between resources (foreign keys, joins).
  • Custom validation logic (regex patterns, custom validators).

API keys

API keys authenticate requests to your generated public endpoints. Each project can issue multiple keys. Keys are scoped to a project: a key for project A cannot read or write project B.

How keys are stored

Only the SHA-256 hash is persisted. The plaintext is shown once at creation and never again. Lose it and you'll need to rotate. This is also why EndpointOS staff cannot recover a key for you; there's nothing to recover.

Sending a key

Either header works:

Authorization: Bearer sapi_live_ab12cd34ef56...
x-api-key: sapi_live_ab12cd34ef56...

Rotation with grace

Click Rotate on a key to issue a new plaintext while the old key stays valid for a configurable grace window (default 24 hours). Roll out the new key in your clients, watch logs to confirm traffic moved over, then finalize the rotation to revoke the old key. Aborting rotation puts you back to the original key.

Revocation

Click Revoke to immediately invalidate a key. Existing requests in-flight complete; new requests with that key return 401.

Consumer-scoped keys

A key can be attached to a named consumer. Requests made with that key are tagged with the consumer in logs and usage, which lets you see "Ada's traffic this month" without instrumenting your application.

Consumers

A consumer represents an end-customer of your API: a person, a tenant, a partner integration. Naming them lets EndpointOS attribute every request, log row, and usage event to the right entity.

Why consumers exist

Without consumers, you know how much traffic your API gets. With consumers, you know who. That distinction matters when one partner runs hot, when you want a per-customer monthly cap, or when you need to revoke access for a specific integrator without taking down the whole API.

Creating a consumer

Open the Consumers tab on a project, click New consumer, give them a name (e.g. "Acme Corp", "iOS app"), an external ID (optional, your own identifier), and a contact email. Then issue an API key attached to this consumer.

Per-consumer surfaces

  • Consumer dashboard. Request volume, status distribution, top endpoints, per-consumer monthly usage.
  • Per-consumer monthly cap. Set in the consumer's settings. When hit, the API returns 429 until the next billing month.
  • Archive / unarchive. Soft-disable a consumer without losing their historical logs.

Limits

EndpointOS enforces several quotas to protect your API from accidental overuse and to make billing predictable. They stack: a request must pass every applicable limit.

Per-project monthly cap

Total successful requests across all keys, all consumers. Set in project settings. When exceeded, all subsequent requests for that month return 429 with a Retry-After header pointing at the start of the next month.

Per-consumer monthly cap

Same shape, scoped to a single consumer. Useful for partner agreements ("Acme can call up to 50k requests/month") or for tiered pricing in your own product.

Per-key per-minute rate limit

Each API key has a per-minute ceiling. Default is generous; tighten in the key's settings if you want stricter shaping. Exceeding returns 429 with a 60-second Retry-After.

Per-IP auth-path throttle

Login, signup, password-reset, and key-validation paths are rate-limited per source IP (60/min). This is platform-level; you don't configure it. It bounds credential-stuffing and key-bruteforce attempts.

Body guards

Public-API request bodies are capped at 1 MiB and 32 levels of JSON nesting. Prototype- pollution payloads (__proto__, constructor.prototype) are stripped at parse time. These limits are platform-level and not configurable.

Billing & subscriptions

Plans are workspace-scoped (one subscription per workspace). Free is permanent; paid tiers attach a Stripe subscription that the workspace owner can manage from /plans.

Upgrades: immediate with prorated charge

Switching to a higher tier fires a prorated invoice today: the difference between what you'd have paid on the new plan for the rest of the current cycle and what you've already paid for the old plan, charged to your card on file the moment you confirm. The new monthly / annual rate kicks in from the next renewal. The new plan's caps take effect immediately.

Downgrades: take effect at end of current cycle

Switching to a lower tier schedules the change for the end of the cycle you already paid for. You keep all the higher-tier benefits until that date, then Stripe automatically rolls you onto the lower-tier price. No surprise refund, no surprise downgrade.

Over-cap on downgrade: projects pause, never delete

If the new plan allows fewer projects than you have active, EndpointOS pauses the excess oldest-first when the downgrade lands. Paused projects return 503 PROJECT_PAUSED from the public API, but every record, resource, key, log, and webhook is preserved exactly as it was. Reactivate any of them from the dashboard banner; reactivation is gated by the new plan's cap, so pick which projects matter and pause / archive others to swap.

Existing resources and API keys keep working past the new plan's caps. New resource / key creation is blocked until you're under cap. API keys are never auto-revoked on downgrade.

Members: pre-flight gate

If the picked plan can't seat your current workspace members, the downgrade is refused at the button with a clear message ("Starter allows 1 seat; this workspace has 4. Remove 3 members first."). Members are reversible by hand; auto-kicking them would be too surprising. Remove the excess members, then try again.

One subscription per workspace

A workspace can only have one active Stripe subscription at a time. Trying to start a new checkout while subscribed routes you to an in-place change with the prorated preview instead of opening a second parallel subscription.

Webhooks

Webhooks notify your systems when records change. Configure an endpoint URL, pick the events to subscribe to, and EndpointOS POSTs a signed JSON payload to your URL.

Creating an endpoint

Open the Webhooks tab on a project, click New endpoint, paste your HTTPS URL, pick the events. The signing secret is shown once. Copy it now.

Signature verification

Every delivery includes an X-EndpointOS-Signature header containing an HMAC-SHA256 of the raw request body, computed with your signing secret. The format mirrors Stripe's pattern (t=timestamp,v1=hex). Verify it before trusting the payload:

const sig = req.headers["x-endpointos-signature"];
const [tsPart, v1Part] = sig.split(",");
const timestamp = tsPart.split("=")[1];
const provided = v1Part.split("=")[1];
const expected = crypto
  .createHmac("sha256", WEBHOOK_SECRET)
  .update(timestamp + "." + rawBody)
  .digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(provided), Buffer.from(expected))) {
  return res.status(401).end();
}

SSRF protection

Endpoint URLs are validated on save and re-validated on every fire. Internal addresses (RFC1918 private ranges, link-local, loopback, IPv4-mapped IPv6) are rejected. This protects your team from accidentally pointing a webhook at an internal service.

Delivery + retries

Each delivery attempt is recorded in the endpoint's delivery log: timestamp, response status, response body (truncated), duration. Today, delivery is fire-and-forget; durable retry with exponential backoff is on the next-up roadmap (see /changelog). Plan accordingly: webhooks are best-effort notifications, not a system of record.

Rotating + disabling

Rotate the signing secret from the endpoint's settings (issues a new secret, invalidates the old). Disable the endpoint to pause all delivery without deleting its history.

Logs & audit

EndpointOS keeps two append-only logs: request logs (every public-API call) and the audit log (every privileged action a user takes in the dashboard).

Request logs

Each row captures method, path, status, latency, key prefix, consumer (if any), source IP, user-agent, request size, and the request body (truncated to 4 KiB). Retention depends on your plan (Free: 7 days).

Filter by method, status class, exact status code, key, consumer, date window, or path substring. Export the filtered view to CSV from the Export button.

Audit log

Every action that creates, modifies, or destroys configuration writes an audit row: who, what, when, from where. Surfaces in account settings. Action names follow a stable noun.verb shape (e.g. api_key.rotated, workspace.member.invited). Filter by action, target type, project, or date range; export to CSV.

What's not logged

  • Response bodies (only sizes). Request bodies are truncated at 4 KiB.
  • Read-only dashboard browsing. Only state-changing actions write audit rows.
  • API key plaintext. The hash is in the request log; the secret never is.

Contracts & versioning

Your project's contract is the OpenAPI 3.1 description of its public API surface: resources, fields, endpoints, validation rules. Every time you publish, a new version of the contract is frozen and made available at a versioned URL.

Why publish?

While you're iterating on schema, the live API tracks the latest unpublished state. The moment you have integrators consuming it, you publish. This freezes a version, locks the OpenAPI, and stamps an entry in your project's changelog. Integrators get a stable URL they can pin to.

Publishing

On the project's Contract tab, click Publish version. EndpointOS diffs the current schema against the previous published version, classifies the change (additive, breaking, no-op), and asks you to confirm the version bump.

What counts as breaking

  • Removing a resource, endpoint, or field.
  • Adding a required field to an existing resource.
  • Tightening a validation rule (narrower enum, tighter bounds).
  • Changing a field type.

Additive changes (new resources, new optional fields, new endpoints, looser bounds) are backwards-compatible.

The API changelog

Each published version writes a Keep-a-Changelog-style entry in your project's public changelog page (endpointos.dev/{username}/changelog/{project}) so your integrators can see what changed between versions without DMing you.

MFA

Two-factor authentication (TOTP) is available for all accounts. Enroll once and every future sign-in challenges for the 6-digit code.

Enrolling

Go to /settings/mfa. Scan the QR code with your authenticator (Apple Passwords, 1Password, Bitwarden, Google Authenticator, or anything else that supports TOTP), enter the current 6-digit code to confirm, and you're done.

Recovery

Lost your authenticator? Email support@baseframelabs.com from the address on your account. We will verify ownership before disabling MFA. There are no recovery codes today; that's a planned addition.

When MFA is enforced

  • Every sign-in for an enrolled account.
  • The admin console refuses to load at single-factor strength when MFA is enrolled.

For everyday workspace use, MFA is optional but recommended if your workspace handles production API keys.

Teams

Projects live inside a workspace. Every account gets a personal workspace on first sign-in. To collaborate, invite teammates into a shared workspace.

Workspaces

Click the workspace name in the dashboard header to open the switcher. Each workspace owns its own projects, resources, keys, logs, consumers, contracts, and webhooks. Switching workspaces does not move projects between them; that's a planned migration flow.

Inviting members

Open Members from the workspace switcher. Owners can invite by email; the recipient gets a single-use link valid for 7 days. They sign up (or sign in, if they already have an EndpointOS account with the same email) and join the workspace.

Roles

  • Owner. Invite and remove members, change roles, transfer ownership (planned), delete the workspace. Plus everything members can do.
  • Member. Create / edit / delete projects, resources, keys, consumers, contracts, webhooks. View logs and audit. Cannot manage workspace membership.

Limits on member management

  • The workspace creator cannot be removed or demoted from the members page.
  • The last remaining owner cannot remove themselves.
  • Invitations are SHA-256-hashed at rest; the plaintext is shown only at creation.
  • An invitation to ada@example.com can only be accepted by a session signed in as ada@example.com.

Something missing?

These docs cover the product as it stands today. New surfaces ship regularly; see the /changelog for what's new. If you can't find what you're looking for, email support@baseframelabs.com and we'll either point you at the right page or add one.

Docs · EndpointOS | EndpointOS