API Keys
API keys are long-lived bearer tokens you create in your BuildWorkPro tenant and use to call the v1 API from your own backend. They’re the right tool for internal scripts, CI jobs, ETL pipelines, and any integration that runs under your team’s control.
If you’re building an app that other BuildWorkPro tenants will install — where each user authorizes you against their own organization — use OAuth 2.1 instead. Don’t ask third parties to paste an API key into your product.
Live vs test keys
Section titled “Live vs test keys”API keys come in two prefixes:
bwp_live_*— Hits real data in your production tenant.bwp_test_*— Reserved for development against a sandbox.
Today, test keys behave the same as live keys (no isolated sandbox tenant exists yet). The prefix is in place so you can future-proof your integration: write your code to read the prefix from the environment, and you’ll be ready when sandbox tenants ship without changing a line.
Creating a key
Section titled “Creating a key”-
In the app, sign in as a user with the
company_adminrole. Only admins can create or revoke keys. -
Open Settings -> Developer -> API Keys and click New Key.
-
Give the key a clear name — something a teammate could recognize months from now (for example, “Zapier sync” or “Nightly QuickBooks export”).
-
Select scopes. Each scope grants read, write, delete, or workflow access to one resource family. Pick the smallest set the integration actually needs — you can always create another key later. See Scopes for the full catalog.
-
(Optional) Restrict the key to a list of source IPs. Useful when the integration runs from known infrastructure.
-
Click Create. The full key is displayed once.
Authenticating requests
Section titled “Authenticating requests”Send the key in the Authorization header as a bearer token:
curl https://app.buildworkpro.com/api/v1/me \ -H "Authorization: Bearer ${BUILDWORKPRO_API_KEY}"A successful call to /me returns the key’s tenant binding and granted scopes — handy for sanity-checking a new key:
{ "data": { "keyId": "key_01HZX9...", "keyName": "Internal sync script", "tenantId": "tnt_01HZ...", "scopes": ["contacts:read", "contacts:write", "leads:read"] }, "meta": { "request_id": "req_2c8a91f4" }}Idempotency on mutations
Section titled “Idempotency on mutations”Always send an Idempotency-Key header on POST, PATCH, and DELETE requests. Use a fresh UUID per logical operation. If a network hiccup makes you retry, BuildWorkPro replays the original response instead of creating a duplicate. See Idempotency for details.
curl -X POST https://app.buildworkpro.com/api/v1/contacts \ -H "Authorization: Bearer ${BUILDWORKPRO_API_KEY}" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"firstName":"Jane","lastName":"Doe","type":"customer"}'IP allowlist
Section titled “IP allowlist”Each key can be restricted to a list of allowed source IPs in the same Settings -> Developer -> API Keys screen. Requests from any other IP are rejected with 401 unauthorized. The allowlist is configured in the UI today; a programmatic API for managing it is on the roadmap.
Best practices
Section titled “Best practices”- Rotate periodically. Quarterly is a reasonable default. Create the new key, deploy it, then revoke the old one once you’ve confirmed the rollout.
- One key per integration. Don’t share a key across two integrations — you can’t revoke one without breaking the other.
- Never ship a key in client-side code. Mobile apps, single-page apps, and browser extensions cannot keep a secret. Use OAuth.
- Use the smallest scope set possible. A reporting script doesn’t need
contacts:delete. - Treat leaks as incidents. If a key is exposed in a screenshot, log file, or git commit, revoke it immediately and rotate downstream.
Revoking a key
Section titled “Revoking a key”In Settings -> Developer -> API Keys, click the key and choose Revoke. Revocation takes effect on the next request — any in-flight requests using that key will fail with 401 unauthorized immediately. Revocation is permanent; create a new key if you need access again.
Browser CORS for Bearer auth (deliberately not supported)
Section titled “Browser CORS for Bearer auth (deliberately not supported)”The /api/v1/* endpoints are designed for server-to-server use with a long-lived bwp_live_... / bwp_test_... key, and they do not participate in CORS for Bearer-authenticated calls from arbitrary browser origins.
A preflight OPTIONS request from a third-party origin to /api/v1/contacts (or any other v1 path) will not receive permissive Access-Control-Allow-Origin headers, so the browser will block the subsequent GET / POST. This is intentional:
- An API key is a high-value credential. Putting it in browser JavaScript exposes it to every script on the page and every browser extension installed for the user. A leaked key has tenant-wide blast radius until rotated.
- Browser-side integrations should use the OAuth 2.1 flow instead. OAuth-issued
bwp_at_...access tokens are scoped to a single user, can be revoked individually, and don’t surface long-lived secrets to client code. The OAuth endpoints (/api/v1/oauth/register,/oauth/token,/oauth/revoke,/oauth/userinfo) are explicitly CORS-enabled for browser clients.
If you have a use case that genuinely requires browser-side API key calls (e.g., a same-origin admin tool hosted at app.buildworkpro.com), the same-origin request already works — CORS only blocks cross-origin calls. For everything else, prefer OAuth.