v1 API

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:

url
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.

SurfaceAuthentication
GET /api/v1/{collection}, public collectionNone, but still not callable from a browser, see above.
GET /api/v1/{collection}, private collectionA key granted entries:read, as a bearer token. A valid key is not sufficient on its own.
GET /api/v1/media/{id} and /metaNone, ever, for any asset: see the note on the Media page.
GET /api/v1/forms/{slug}None.
POST /api/v1/forms/{slug}/submitNone, and no key should ever be sent to it.
/api/mcpA valid API key. Every tool, reads included, is gated on the grants that key holds.
GET /api/dashboard/sdkA 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.

http
Authorization: Bearer pk_3f7a91c4e2b85d06...
bash
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
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.

ResourceNameCovers
collectionsCollectionsCreate, alter and delete collection definitions and their fields
entriesEntriesCreate, edit, publish and delete entries in every collection
mediaMediaUpload, list and delete assets in the media library
formsFormsCreate, configure and delete forms and their fields
submissionsSubmissionsRead, edit and delete form submissions and their file uploads (PII)
usersUsersList, create, deactivate and delete the workspace's end users (PII)
rulesRulesCreate, edit and delete reusable rules — used today to gate access to collections
sitesSitesDeploy 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.

Reads are grants too

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:

  • write implies read on the same resource. A key that can change a row but not read it back is not a capability anyone wants.
  • Any grant on entries implies collections:read, because every entry operation resolves its collection definition first.
  • Any grant on submissions implies forms: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.

RoleMeans
ownerBypasses 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.
memberHolds 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.