Introduction

The Bodek API gives you one base URL for every service. Each service has its own per-service key, created in the developer console.

Base URL   https://dev.bodek.us/v1/<service>/…
Services   files · forms · iam · users
Format     JSON over HTTPS (file uploads use multipart/form-data)

Signing people in to your app with their Bodek account is a separate product — Bodek OAuth — that uses OAuth client credentials instead of API keys.

API catalog

APIBaseEndpoint groupsCredentials
Files /v1/files Files (move, copy, compress, unzip), Folders, Chunked uploads, Search, Shares, Storage, Workspaces, Members & invites, Webhooks Files key
Forms /v1/forms Forms (create, update, duplicate, open/close, delete), Stats, Responses (filter, export), Uploads & file download, Invitations, Embed Forms key
IAM /v1/iam Workspace members — list, invite, change role, remove; pending invites and invite codes IAM key
Users /v1/users Create users, Create businesses, Workspace invitations, Password resets Users key (approval required)
SMS sms.bodek.us/api/v1 Send & read messages, Numbers, Groups SMS key (plan add-on) — manage
Bodek OAuth accounts.bodek.us Discovery, Authorize, Token, UserInfo, Introspection, Revocation OAuth client id / secret

Each API page is the authoritative, endpoint-by-endpoint reference. For uptime checks, GET /v1/files/health and GET /v1/forms/health answer without a key.

Getting access

  1. Sign in to this site with your Bodek account (Sign in). The developer console requires an active Bodek plan; if yours isn't active you'll be sent to billing.
  2. Create a key in the console for the service you want — Files, Forms and IAM keys are self-service.
  3. Users API only: submit an access request first. After an administrator approves it, the Users service becomes available when creating keys.
  4. For “Sign in with Bodek”, register an app in the OAuth apps console instead — see the OAuth guide.

API keys

Keys look like bf_live_ or bf_test_ followed by 32 characters (A–Z, 2–7). The full key is shown once, when you create it; only a hash is stored, and the console shows its prefix afterwards. When creating a key you choose:

SettingDetails
Servicefiles, forms, iam or users. A key can only call its own service.
NameYour label, up to 120 characters.
Environmentlive or test. Both hit the same data and behave identically; the prefix just makes them easy to tell apart.
ScopesAt least one, from that service's list (see Scopes & services).
WorkspaceFiles keys act on your personal workspace or one organization workspace you belong to. IAM keys must target an organization workspace. Forms and Users keys aren't tied to a workspace.
Folder lockFiles keys only, optional: restrict the key to one folder (and everything inside it). The folder becomes the key's root — listings without a parent start there, uploads without a parent land there, and anything outside it returns 404.
Allowed originsOptional comma-separated domains, e.g. app.example.com, *.example.com. Blank means any origin. See Origin restrictions.
Rate limitsRequests per minute (default 120, max 1,000) and per rolling 24 hours (default 20,000, max 1,000,000). Users API keys are capped lower (30/minute, 1,000/day). See Rate limits.

Scopes, workspace and origins can't be edited later — create a new key instead. Revoke disables a key immediately (requests then fail with 401 token_revoked) but keeps it listed; Delete removes it entirely.

Authentication

Send your key as a bearer token on every request:

curl https://dev.bodek.us/v1/files/files \
  -H "Authorization: Bearer bf_live_ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"

Treat keys like passwords: keep them server-side, and if one leaks, revoke it in the console and create a new one.

Sign in with Bodek

Separate from API keys, Bodek Accounts is an OAuth 2.0 provider with OpenID Connect–style discovery and UserInfo. Add a “Sign in with Bodek” button so people log into your site with their Bodek account — password and two-factor handled entirely by Bodek, no credentials touching your app.

Register a client in the OAuth apps console and follow the integration guide for the authorization-code and PKCE flows, refresh tokens, UserInfo, introspection and revocation.

Scopes & services

These are all the scopes you can pick when creating a key. A request needs the scope listed on its endpoint in the API reference; otherwise it fails with 403 insufficient_scope.

ServiceBaseScopes
Files/v1/files files:read files:write files:delete folders:write share:read share:write workspace:read workspace:write webhooks:read webhooks:write
Forms/v1/forms forms.read forms.submit forms.write
IAM/v1/iam iam:read iam:write
Users/v1/users users:create business:create workspace:invite users:password_reset
SMSsms.bodek.us/api/v1 messages:send messages:read numbers:read groups:read groups:write

Note the separators: Forms scopes use a dot (forms.read), the others a colon.

Response format

Files, Forms and IAM wrap successful payloads in data, with optional meta for pagination:

{
  "data": [ … ],
  "meta": { "limit": 25, "offset": 0, "total": 134 }
}

The Users API returns its result object at the top level (see Users conventions). Deletes may return 204 No Content with an empty body. File downloads return the raw bytes with the file's Content-Type and a Content-Disposition header.

Errors, on every service, carry a stable machine code and a human message, plus details when there's more to say:

{
  "error": {
    "code": "insufficient_scope",
    "message": "This key needs the scope: forms.submit",
    "details": { "required": ["forms.submit"] }
  }
}

Branch on error.code, not on the message text.

Pagination

List endpoints return a page at a time and describe it in meta:

  • Forms — limit and offset query parameters; meta has limit, offset and total. Request the next page with offset = offset + limit until you've read total items.
  • Files — limit (1–100, default 20) and offset, with an optional sort (name_asc or name_desc). Follow meta.next_offset and stop when it is null; the Files reference lists exactly which parameters each list endpoint takes.

Errors

StatusMeaningCommon codes
400Bad request — malformed or invalid input.invalid_json, endpoint-specific validation codes
401Missing, malformed, unknown, expired or revoked key.missing_authorization invalid_token_format invalid_token token_expired token_revoked
403Wrong service, missing scope, blocked origin, or no access.wrong_service insufficient_scope origin_blocked origin_required ip_blocked access_not_granted
404No such endpoint or resource (or it's outside the key's workspace or folder).not_found unknown_service
405The endpoint exists but not with that method.method_not_allowed
409Conflict — e.g. a closed form, a taken email, an existing member.endpoint-specific
413Upload too large.upload_failed
422Validation failed (see error.details).endpoint-specific
429Rate limit exceeded.rate_limited
500Unexpected server error — retry, then report it with the request id.internal_error server_error
502The gateway couldn't reach the service backend — retry with backoff.upstream_unreachable

Request IDs

Every response from a service includes an X-Request-Id header (a 20-character hex string). It is also recorded in your key's audit log. Log it on your side and quote it in bug reports so we can find the exact request.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Request-Id: 8069ab8f752950692a1c
X-RateLimit-Limit-Minute: 120
X-RateLimit-Remaining-Minute: 119
X-RateLimit-Reset-Seconds: 42
X-RateLimit-Limit-Day: 20000
X-RateLimit-Remaining-Day: 19873

Rate limits

Each key has a per-minute cap and a rolling 24-hour cap, set when you create it (defaults 120/minute, 20,000/day). Every authenticated request counts, including ones that fail. Responses carry:

HeaderMeaning
X-RateLimit-Limit-Minute / X-RateLimit-Remaining-MinutePer-minute cap and what's left of it.
X-RateLimit-Reset-SecondsSeconds until the per-minute window resets (Files, Forms, IAM).
X-RateLimit-Limit-Day / X-RateLimit-Remaining-Day24-hour cap and what's left of it.

Over a cap you get 429 rate_limited with error.details.limit. Wait for the reset (or back off exponentially) before retrying. The console shows each key's usage over the last 24 hours.

Origin restrictions

A key with no allowed origins works from anywhere. A key with allowed origins only accepts requests whose Origin header — or, if absent, Referer — has a matching host:

  • app.example.com matches that host exactly.
  • *.example.com matches any subdomain and example.com itself.
  • A request with neither header (a typical server-side call) is rejected with 403 origin_required; a non-matching host gets 403 origin_blocked.

So use origin-restricted keys for browser code (publishable keys) and unrestricted keys, kept secret, on your servers. Origin checks happen in the service and work the same through the gateway and the direct hosts. (Server-only and IP-allow-list modes exist for keys provisioned by Bodek; IP-restricted keys should call the direct hosts rather than the gateway.)

CORS & browsers

The gateway supports cross-origin calls from browsers. It answers OPTIONS preflights with 204 and echoes the caller's Origin in Access-Control-Allow-Origin (no cookies — Access-Control-Allow-Credentials: false; authenticate with the Authorization header).

Access-Control-Allow-Methods:  GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers:  Authorization, Content-Type, Accept, Range, If-Range, If-None-Match,
                               If-Modified-Since, If-Match, Idempotency-Key, X-Requested-With
Access-Control-Expose-Headers: X-Request-Id, X-RateLimit-*, Content-Disposition, Content-Range,
                               Accept-Ranges, ETag, Last-Modified, Location, Retry-After
Access-Control-Max-Age:        86400

CORS only lets the browser read the response; whether the request is allowed is decided by the key's origin restrictions. Never ship an unrestricted key in browser code.

Gateway & direct hosts

Use the gateway unless you have a reason not to. It forwards the method, path, query string, body (JSON, raw, or multipart uploads), Authorization, and download/caching headers (Range, If-None-Match…) to the service, and relays the status code, body and response headers such as Content-Type, Content-Disposition, Content-Range, ETag, Location, X-Request-Id and X-RateLimit-*. Redirects (for example a 302 to a temporary download URL) are passed back to your client to follow.

ServiceGatewayDirect host
Fileshttps://dev.bodek.us/v1/files/…https://files.bodek.us/api/v1/…
IAMhttps://dev.bodek.us/v1/iam/…https://files.bodek.us/api/v1/iam/…
Formshttps://dev.bodek.us/v1/forms/…https://forms.bodek.us/api/v1/forms/… (service endpoints /health, /me at https://forms.bodek.us/api/v1/…)
Usershttps://dev.bodek.us/v1/users/…— (served by the gateway itself)

The same key works on both. The gateway allows up to 60 seconds per request; for very large uploads or downloads, or IP-restricted keys, call the direct host. An unknown service name returns 404 unknown_service.

Migrating from file.bodeksolutions.com

Existing Files API keys keep working. Swap the base URL — https://file.bodeksolutions.com/api/v1 → https://dev.bodek.us/v1/files — and leave every resource path (/files, /folders, /shares/{id}) unchanged.