Getting started
Authentication
Sedgemark authenticates API traffic with bearer tokens called API keys. Most of the API needs no key at all; this page is about knowing which parts do.
Your base URL
Every workspace is addressed by its own subdomain, and there is no account id, project id or organization segment in any path. The hostname is the scope:
https://{your-workspace}.sedgemark.app
A request to a hostname that names no workspace returns 404 { "error": "Unknown tenant" }, which is how you tell a wrong host
apart from a wrong path.
What actually needs a key
Most integrations need fewer keys than people expect. Note that “needs no key” is not the same as “callable from a browser”: the content and media JSON endpoints send no CORS headers and are meant to be called from a server or a build step regardless of visibility. See Content API → This is a server-side API.
| Surface | Authentication |
|---|---|
GET /api/v1/{collection}, public collection | None, but still not callable from a browser, see above. |
GET /api/v1/{collection}, private collection | A key granted entries:read, as a bearer token. A valid key is not sufficient on its own. |
GET /api/v1/media/{id} and /meta | None, ever, for any asset: see the note on the Media page. |
GET /api/v1/forms/{slug} | None. |
POST /api/v1/forms/{slug}/submit | None, and no key should ever be sent to it. |
/api/mcp | A valid API key. Every tool, reads included, is gated on the grants that key holds. |
GET /api/dashboard/sdk | A dashboard session, or a key granted collections:read. |
Creating a key
In the dashboard, go to Users & API Keys, open the API Keys tab, and create one. Give it a name describing where it will live: astro build, staging preview, because that name is all you will have to go on when deciding whether a key is still needed.
Keys look like pk_ followed by 64 hexadecimal characters. The full value is
shown to you exactly once, at
creation. Sedgemark stores only a SHA-256 hash of it, so a lost key cannot be recovered or
re-displayed by anyone, including an administrator; revoke it and create another.
Each key records a last_used_at timestamp, updated at most once a minute, so
you can find keys nothing uses any more before you rotate them.
Keys do not expire. A key works until it is revoked, and revoking is immediate and permanent: there is no grace period and no un-revoke. Revoke from the same screen you created it on. Rotating a key means creating the new one, moving your integrations across, then revoking the old one; nothing rotates automatically.
Sending a key
One header, on any request that needs it. There is no query-parameter form and no cookie form.
Authorization: Bearer pk_3f7a91c4e2b85d06... curl https://acme.sedgemark.app/api/v1/blog_post \
-H "Authorization: Bearer $SEDGEMARK_API_KEY"
A missing, malformed or unknown key on an endpoint that requires one returns 401:
HTTP/1.1 401 Unauthorized
{ "error": "Unauthorized" } Sending a valid key to a public collection is harmless; it is simply ignored. That is what lets one client read public and private collections without branching on which is which.
Permissions
A key carries a set of grants, chosen
with checkboxes when you create it. A grant is a resource paired with an action, read or write, and a key may hold any combination of them, so
a single key can serve an integration that needs several capabilities.
| Resource | Name | Covers |
|---|---|---|
collections | Collections | Create, alter and delete collection definitions and their fields |
entries | Entries | Create, edit, publish and delete entries in every collection |
media | Media | Upload, list and delete assets in the media library |
forms | Forms | Create, configure and delete forms and their fields |
submissions | Submissions | Read, edit and delete form submissions and their file uploads (PII) |
users | Users | List, create, deactivate and delete the workspace's end users (PII) |
rules | Rules | Create, edit and delete reusable rules — used today to gate access to collections |
sites | Sites | Deploy the workspace’s static site and roll it back — see the sedgemark CLI |
Written out, a grant looks like entries:read or forms:write, and
that is the spelling used by every error message that refuses one.
There is no operation a key gets merely by being valid. A key holding no grants can authenticate and do nothing: every private endpoint and every MCP tool checks for a specific grant, reads included. This is the part most likely to surprise you if you built against an earlier version of this API, where any valid key could read private content and call every MCP read tool.
Keys created before this model existed were migrated to the equivalent grants, so nothing that was working stopped working.
Three implications, and nothing else
The set you check is expanded when a request is checked, never rewritten in storage. Exactly three rules apply:
-
writeimpliesreadon the same resource. A key that can change a row but not read it back is not a capability anyone wants. -
Any grant on
entriesimpliescollections:read, because every entry operation resolves its collection definition first. -
Any grant on
submissionsimpliesforms:read, because a submission cannot be interpreted without the form it belongs to.
No implication ever confers a write.
A key holding entries:write can create and publish rows in every collection
and still cannot alter a single field definition: that separation is the point of the
model. The arrows also run one way: collections access implies nothing
about entries.
Submissions are their own grant
forms:read lets a key list a form’s fields. Reading what people
actually submitted through it needs submissions:read, which is separate and
is never implied by anything else. Submissions are personal data, and “what does
this form collect?” and “what has it collected?” deserve different
answers.
The separation holds past the obvious places. A key with forms:write that
attempts a schema change stored submissions would violate is refused, and the refusal
withholds how many rows are in the way unless the key also holds submissions:read. A count is aggregate data about the inbox, and differencing
counts across a list of options recovers a distribution.
An entries key sees more over MCP than over HTTP
The delivery API never returns drafts, whatever a key holds. The MCP list_items and get_item tools deliberately do: an agent needs
to see the draft it just created in order to check it before publishing.
So a key granted entries:read for “just the published content”
can read your unpublished drafts if someone points an MCP client at your workspace. Say so
when you hand one out, or do not hand it out.
Handling keys
- Never ship a key to a browser. Client-side JavaScript, a public repository and a static site’s built output are all readable by anyone. Keep keys in server environments, build-time environment variables, and agent configuration.
- One key per consumer. Revoking a shared key breaks everything that shared it; revoking a named one breaks the one thing you meant to break.
- Prefer public collections for anything a visitor will see anyway. A key protecting content that is rendered publicly adds a secret to manage without adding secrecy.
- Never send a key to a form endpoint. Form submission is unauthenticated by design and runs in browsers; attaching a key there publishes it.
Dashboard users are a separate thing
API keys authenticate machines. People sign in to the dashboard with an email and password, and hold one of two roles, and the role decides exactly one thing: whether a permission set is consulted at all.
| Role | Means |
|---|---|
| owner | Bypasses the permission set entirely. Additionally holds the surfaces that are deliberately not expressible as a grant: inviting and removing users, creating and revoking API keys, webhooks, and the plan and payment details. |
| member | Holds precisely the grants it was given, and nothing else: the same eight resources an API key is scoped by, checked the same way. A member with no grants can sign in and see an empty dashboard. |
This is the same model as the table above, applied to a person instead of a key. That is deliberate: “may I do this?” has one answer regardless of whether the caller arrived over the dashboard, the delivery API or MCP.
A few surfaces are owner-only and cannot be granted to a member at all: users, API keys, webhooks, billing and identity settings. Those carry credentials or money, so they are not one checkbox away from anybody.
Changing what someone holds takes effect on their next token refresh, within the hour, without them signing out.
A role never turns into an API key and an API key never turns into a session. The two systems do not overlap, which is why a compromised key cannot be used to sign in to the dashboard.
A third kind of auth: account sessions
API keys and dashboard sessions both authenticate you, the person or system building on Sedgemark. Workspaces with Identity enabled have a third, separate credential for a third kind of caller: the tenant’s own end users, signing in to the tenant’s own site.
An account session is an opaque bearer token returned by POST /api/v1/accounts/login, sent the same way an API key is: Authorization: Bearer, but it is not an API key: it carries no permission
grants, identifies one end user, and is the credential a gated collection’s read endpoint
accepts in place of a key. It is fixed at 30 days with no renewal; signing in again is
how a visitor gets a new one.
Full endpoint list, signup and session lifecycle: Identity.