On this page and tools
Start here
Agent skill and integration brief
Two files an AI agent can load before it writes a line of code: an installable skill, and the same facts as a plain brief you can paste into any agent. Both are generated from the code the API runs on, so they do not drift from it.
Get them
| File | For | Address |
|---|---|---|
| SKILL.md | Tools that load skills, such as Claude Code. | /docs/agent-skill?format=skill |
| Integration brief | Any agent. Paste it into the conversation. | /docs/agent-skill?format=brief |
| OpenAPI 3.1 spec | Tools that build clients from a spec. | /docs?format=openapi |
Install the skill
- Save the SKILL.md file as
~/.claude/skills/bynefit-api/SKILL.md, or in a project's.claude/skills/bynefit-api/folder. - Put your key in the environment variable
BYNEFIT_API_KEY. The skill tells the agent never to print it. - Ask the agent to build against a test key first.
Fetch it again whenever you upgrade. The file says what it was generated from, and a live response always wins over the file.
What is in them
- The base URL, how to authenticate, and the rules an agent should follow with your key.
- Every call in the spec: method, path, what it needs, and whether it takes an
Idempotency-Key. - Every error code with its status and meaning, and the
request_idto quote. - How safe retries work, and the rate limits.
- How to verify a signed webhook and de-duplicate replays.
The skill, as served
SKILL.mdmarkdown
---
name: bynefit-api
description: Use when building, testing or debugging an integration with the Bynefit API: reading a Space, sending partner events, inviting businesses, receiving webhooks, or signing members in with Bynefit.
---
# Bynefit API
Generated from the same code the API runs on. If this file and a live response disagree, trust the response and tell the person you are working for.
## Where things are
- Base URL: `https://bynefit.com/dash/api/v1`
- OpenAPI 3.1 spec: `https://bynefit.com/docs?format=openapi`
- Docs index for agents: `https://bynefit.com/docs?format=llms`
- Any docs page as Markdown: add `?format=md`, or send `Accept: text/markdown`
- Docs search over MCP: `https://bynefit.com/docs?format=mcp`
## Rules
1. Read the key from the environment variable `BYNEFIT_API_KEY`. Never print it, log it, commit it or put it in a URL.
2. Start with a test key: a `bapi_tk_test_` or `bapi_pk_test_` key answers like the real API with sample data (`X-Bynefit-Mode: test`, `"livemode": false`). An agent key's own test mode is different: it reads the owner's real data, read-only, on a smaller daily ceiling. Calls cost units: read `X-Bynefit-Units-Remaining`, and on `402 usage_exhausted` stop and tell your owner (the answer carries a `top_up_url`).
3. With a live key, make read calls only. Ask the person before any call that creates, changes or deletes data.
4. Send `Authorization: Bearer <key>` on every call. Partner calls are different: see the partner rows below.
5. Follow `next_cursor` until `has_more` is false. Never build a cursor yourself.
6. On `429`, wait for `Retry-After`. On `5xx`, retry with the same `Idempotency-Key` where the call accepts one.
7. Quote the `request_id` from any error when you report a problem.
8. If a response does not match the spec, stop and show the person the request and the response.
## Calls
| Call | What it does | Needs | Idempotency-Key |
| --- | --- | --- | --- |
| `GET /tasks` | List tasks, newest first | key with read:tasks | no |
| `GET /tasks/{id}` | Get one task | key with read:tasks | no |
| `GET /users` | List members of the Space | key with read:users | no |
| `GET /users/{id}` | Get one member | key with read:users | no |
| `GET /users/{id}/membership` | Get a member's membership in the Space | key with read:users | no |
| `GET /events` | List events from the event log, newest first | key with read:events | no |
| `GET /events/{id}` | Get one event with its data | key with read:events | no |
| `GET /forms` | List forms | key with read:forms | no |
| `GET /forms/{id}` | Get one form | key with read:forms | no |
| `GET /forms/{id}/submissions` | List submissions of a form | key with read:forms | no |
| `GET /forms/{id}/submissions/{submission_id}` | Get one submission | key with read:forms | no |
| `GET /payments` | List payments, newest first | key with read:payments | no |
| `GET /payments/{id}` | Get one payment | key with read:payments | no |
| `GET /teams/current` | Get the Space the key belongs to | any key | no |
| `GET /resources` | List the public community resources directory | key with read:resources:public | no |
| `GET /agent` | Describe the agent key making the call | any key | no |
| `POST /agent/access-requests` | Ask the key owner for one more permission | any key | yes |
| `GET /sites` | List the hosted sites in the Space | key with read:site-files | no |
| `GET /sites/{site_id}/files` | List the files of a site | key with read:site-files | no |
| `GET /sites/{site_id}/files/{path}` | Read one site file and its sha256 | key with read:site-files | no |
| `GET /sites/{site_id}/versions/{path}` | List the saved versions of a site file | key with read:site-files | no |
| `PUT /sites/{site_id}/files/{path}` | Create or replace a site file | key with write:site-files | yes |
| `DELETE /sites/{site_id}/files/{path}` | Move a site file to the trash | key with write:site-files | yes |
| `POST /forms` | Create a form | key with write:forms | yes |
| `PATCH /forms/{id}` | Change a form's settings | key with write:forms | no |
| `DELETE /forms/{id}` | Delete a form that has no submissions | key with write:forms | no |
| `POST /forms/{id}/fields` | Add a field to a form | key with write:forms | no |
| `PATCH /forms/{id}/fields/{field_id}` | Change a field of a form | key with write:forms | no |
| `DELETE /forms/{id}/fields/{field_id}` | Delete a field of a form | key with write:forms | no |
| `POST /forms/{id}/fields/{field_id}/move` | Move a field up or down | key with write:forms | no |
| `GET /engagement-types` | List the Space's engagement types | key with read:engagement-types | no |
| `GET /engagement-types/{type_key}` | Get one engagement type | key with read:engagement-types | no |
| `POST /engagement-types` | Create an engagement type | key with write:engagement-types | yes |
| `PATCH /engagement-types/{type_key}` | Change an engagement type | key with write:engagement-types | no |
| `DELETE /engagement-types/{type_key}` | Delete an engagement type no engagement uses | key with write:engagement-types | no |
| `POST /beacons` | Send a batch of events from your server | key with write:beacons | no |
| `GET /beacons/summary` | Read the event summary, funnel and credits | key with read:beacons | no |
| `POST /beacons/collect` | Send events from a browser or a signed server call | partner key (browser) or signature | no |
| `POST /partner/businesses` | Invite a business owner to set up a Space | partner key and signature | yes |
| `GET /partner/businesses/{business_ref}/promotions` | List the promotions you sent for a business | partner key and signature | no |
| `POST /partner/businesses/{business_ref}/promotions` | Send a promotion for a claimed business, held for the owner to approve | partner key and signature | yes |
## Errors
Every error has `error` (a stable code), `message`, `request_id` and `docs_url`. The same id is in `X-Request-Id`.
| Status | Code | Meaning |
| --- | --- | --- |
| 401 | `missing_or_invalid_token` | The key is missing, malformed or unknown. |
| 401 | `token_revoked` | The key was revoked. Make a new one. |
| 401 | `token_expired` | The key passed its expiry date. Make a new one. |
| 401 | `missing_or_invalid_key` | The partner key is missing or not recognised. |
| 401 | `key_in_url` | A write endpoint does not take a key in the address. Send it as an Authorization Bearer header and revoke the key that was exposed. |
| 401 | `bad_signature` | The request signature did not match. Sign the exact body you send. |
| 403 | `insufficient_scope` | The key is valid but not scoped for this endpoint. The response names the missing scope in required. |
| 403 | `domain_not_authorized` | The request came from a web address this key is not allowed on. |
| 403 | `origin_not_allowed` | The request origin is not allowed for this key. |
| 403 | `not_a_partner` | The Space is not enrolled as a partner. |
| 403 | `partner_paused` | The partner is paused, so this call is refused until it is resumed. |
| 404 | `not_found` | There is nothing with that id, or the key cannot see it. |
| 404 | `not_enabled` | This feature is not switched on yet. |
| 404 | `unknown_resource` | There is no such resource in v1, or this key type cannot reach it. |
| 404 | `unknown_subresource` | There is no such sub-resource under that resource. |
| 405 | `method_not_allowed` | That HTTP method is not supported on this endpoint. The Allow header lists the ones that are. |
| 400 | `invalid_json` | The request body is not valid JSON. |
| 400 | `events_required` | The body needs a non-empty events list. |
| 400 | `invalid_id` | The id is not in the expected format for this resource. |
| 400 | `invalid_since` | since must be an ISO-8601 date-time. |
| 400 | `invalid_cursor` | The cursor is not one this endpoint issued. Start again without it. |
| 400 | `invalid_sort` | The sort value is not one this endpoint supports. |
| 400 | `invalid_offset` | offset must be a whole number of zero or more. |
| 400 | `invalid_paging` | The paging parameters are not valid together. |
| 400 | `invalid_assignee` | The assignee is not a member of this Space. |
| 413 | `batch_too_large` | The request carries more items than one call accepts. Split it. |
| 413 | `body_too_large` | The request body is larger than this endpoint accepts. |
| 400 | `invalid_idempotency_key` | Idempotency-Key must be 8 to 255 printable characters with no spaces. |
| 409 | `idempotency_key_reused` | That Idempotency-Key was already used with a different request. Use a new key for a new request. |
| 409 | `idempotency_in_progress` | The first request with this Idempotency-Key is still running. Retry after Retry-After. |
| 503 | `idempotency_unavailable` | Idempotency-Key cannot be honoured right now, so nothing was done. Retry later. |
| 402 | `usage_exhausted` | The Space has used all its agent units and has no pack credit, extra usage or automatic top up. Stop and tell the owner; top_up_url in the body is where they add units. |
| 429 | `rate_limited` | Too many requests in the trailing hour. Wait for Retry-After. |
| 429 | `resend_too_soon` | That invitation was sent recently. Wait for Retry-After. |
| 500 | `server_error` | Something failed on our side. Retry, and quote the request_id if it keeps happening. |
## Safe retries
Send `Idempotency-Key: <unique value, 8-255 printable characters>` on the calls marked yes above. Retrying the same request with the same key within 24 hours returns the first answer with `Idempotent-Replayed: true` and does the work once. The same key with a different body is `409 idempotency_key_reused`. Keys belong to your own key and are never shared across keys or Spaces. A `5xx` or `429` is not stored, so retry those with the same key.
## Limits
- Developer key: 60 calls per hour
- Free Space key: 1,000 calls per hour
- Paid Space key: 10,000 calls per hour
- Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`.
## Permissions (scopes)
- `read:events`: Activity
- `read:tasks`: Tasks
- `read:users`: Member profiles
- `read:forms`: Forms and answers
- `read:payments`: Payments
- `read:*`: Everything readable
- `read:users:pii`: Member emails (by approval: ask the key owner)
- `embed:widget`: Website widget
- `write:resources`: Manage your directory (by approval: ask the key owner)
- `write:beacons`: Send product events (sensitive: named explicitly, never covered by a wildcard)
- `read:beacons`: Read event totals (sensitive: named explicitly, never covered by a wildcard)
- `read:resources:public`: read:resources:public
- `write:resources:public`: write:resources:public
- `read:site-files`: Read your site files (sensitive: named explicitly, never covered by a wildcard)
- `write:site-files`: Change your site files (sensitive: named explicitly, never covered by a wildcard)
- `write:forms`: Create and manage your forms (sensitive: named explicitly, never covered by a wildcard)
- `read:engagement-types`: Read your engagement types (sensitive: named explicitly, never covered by a wildcard)
- `write:engagement-types`: Create and manage your engagement types (sensitive: named explicitly, never covered by a wildcard)
A key only works for the permissions it was made with. To get one more, an agent key calls `POST /agent/access-requests` and the owner decides.
## Webhooks
Events reach your server signed: `X-Bynefit-Signature: sha256=<hex>` is HMAC-SHA256 of `<X-Bynefit-Timestamp>.<raw body>` with the endpoint secret. Verify it, and reject old timestamps. `X-Bynefit-Event-Id` is stable across retries and replays, so use it to de-duplicate. A replayed delivery carries `X-Bynefit-Replay: true`. Send a test event from the endpoint's page to learn the fields before anything real happens.
What exists and what does not
- Read community resourcesList and filter community resources with a developer key. JSON, cursor paging.Available
- Submit a resource for reviewPropose a new entry. A moderator reviews it before it goes live.Available
- Partner eventsPartners send their own product events from a page, a server or an app, and read the totals back. Bynefit turns it on per partner.By approval
- Hosted sites runtimeEvents, resources, media, forms and page views for a hosted site, with no key.Available
- Read and edit hosted site filesAn agent key lists a Space's sites and reads, replaces and deletes their files, with versions, secret scanning and a daily write ceiling. The key owner approves the permission.Available
- Create and manage formsAn agent key creates forms for a Space, changes their settings and fields, pauses them and deletes unanswered ones, with a daily write ceiling and a test mode. Registration forms and submissions stay out of reach. The key owner approves the permission.Available
- Engagement types and stagesA Space defines its own kinds of client work with ordered stages inside the existing statuses. Managers edit them on the Engagements page, and an agent key reads and manages them with a daily write budget and a test mode. The key owner approves the permissions.Available
- Client portal APIA signed-in client reads their own engagements, documents and messages; a practice manager runs the pipeline.Available
- Engagements through an API key or a webhookRead engagements from your own server, or hear about a change, without a person signed in.Planned
- Every docs page as MarkdownAdd ?format=md to any docs page, or send Accept: text/markdown.Available
- llms.txt indexA short index of the docs for tools that read llms.txt, at /docs?format=llms.Available
- OpenAPI specAn OpenAPI 3.1 description of the v1 API, with the error envelope and security schemes, at /docs?format=openapi.Available
- Agent skill and integration briefAn installable SKILL.md and a paste-in brief, generated from the same code as the spec.Available
- Agent keysA key made for an AI agent: read-only to start, short-lived, test mode first, every call logged, and the owner approves anything more.Available
- Request id and docs link on every errorEvery error body carries an id to quote to support and a link to the page that explains it.Planned
- Test keys and a sandboxKeys that only touch sample data, ready at once with no review, so a first attempt cannot break anything.Available
- Idempotency-Key on writesRetry a v1 write without doing it twice: the first answer is replayed for 24 hours, per key.Available
- Status pageLive state, 90-day uptime and incident history for the API, sign-in, webhook delivery, the widget, hosted sites and email. Atom and JSON feeds.Available
- Docs MCP serverRead-only search of these docs from an AI tool.Available