Organization API
Copy page
The key-authenticated /api/v1/org/* API for servers and SDKs acting as the whole organization: list/create/revoke any pass, list facilities, and read the member directory. Scopes org:passes:*, org:facilities:read and org:members:*.
Updated
The organization API is the server-to-server data plane: endpoints for a backend or SDK acting as the whole organization rather than a single signed-in user. Authenticate with an organization API key and org:* scopes.
Base: https://entrybit.net · Auth: Authorization: Bearer eb_sk_… (or X-API-Key) · see Authenticating requests.
It mirrors the user-delegated passes API, but organization-wide: it lists and revokes any pass in the company, adds a facilities endpoint SDKs need to create passes, and exposes a read-only member directory (who is in the organization). Everything stays tenant-scoped — a key only ever touches its own organization’s data.
List all passes
GET /api/v1/org/passes?limit&cursor&searchScope: org:passes:read. Every non-deleted pass in the organization, keyset-paginated newest first. Rows include created_by (the inviting user’s id) for attribution.
Parameters: limit (1–100, default 30), cursor, search — see pagination.
curl "https://entrybit.net/api/v1/org/passes?limit=50" \
-H "Authorization: Bearer $ENTRYBIT_API_KEY"{
"success": true,
"items": [
{ "public_id": "gst_9f1c…", "first_name": "Dana", "status": "checked_in", "arrival_date": "2026-07-12", "facility_name": "North Gate", "created_by": 90210 }
],
"total": 340,
"next_cursor": "eyJpZCI6…",
"has_more": true
}The Pass object is identical to the user-delegated API. (The usage allowance block is a user-delegated concept and does not appear here.)
Create pass(es)
POST /api/v1/org/passes
Content-Type: application/jsonScope: org:passes:write. Creates 1–10 passes with the same request body, validation, quota, QR and delivery as POST /api/v1/passes. Attribution and the invite quota go to the admin who minted the key.
curl -X POST https://entrybit.net/api/v1/org/passes \
-H "Authorization: Bearer $ENTRYBIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Dana",
"email": "dana@example.com",
"arrival_date": "2026-07-12",
"facility_id": 42,
"quantity": 1
}'Returns 201 with the same PassCreateResponse shape (public_id, qr_values, pass_link, qr_sent, sms_sent, …). Errors: 400 validation, 402 quota, 404 facility not found, 409 facility deactivated or suspended.
Revoke any pass
DELETE /api/v1/org/passes/{public_id}Scope: org:passes:write. Cancels the pass and deactivates its QR credential — regardless of which user created it (still scoped to the key’s organization).
{ "success": true, "public_id": "gst_9f1c2ab34cd56ef7", "revoked": true }Returns 409 when the pass is no longer active, 404 when no such pass exists in the organization.
List facilities
GET /api/v1/org/facilitiesScope: org:facilities:read. The facility ids an SDK needs before it can create passes.
curl https://entrybit.net/api/v1/org/facilities \
-H "Authorization: Bearer $ENTRYBIT_API_KEY"{
"success": true,
"facilities": [
{ "id": 42, "name": "North Gate", "description": null, "is_active": true, "suspended": false }
]
}The Facility object
| Field | Type | Notes |
|---|---|---|
id | integer | Use as facility_id when creating passes. |
name | string | |
description | string | null | |
is_active | boolean | |
suspended | boolean | Suspended facilities pause new invites. |
List members
GET /api/v1/org/members?limit&cursor&search&fields&statusScope: org:members:read — plus org:members:contact:read to read contact fields. The organization’s member directory, keyset-paginated newest first. Active members only by default — pass status=inactive or status=all to include deactivated members. Guest shadow rows never appear.
Parameters: limit (1–100, default 30), cursor — see pagination; search — case-insensitive match on the member’s name (and on email/phone when the credential carries the contact scope); fields — a comma-separated subset of the Member object (see field selection); status — active (default), inactive, or all.
curl "https://entrybit.net/api/v1/org/members?limit=50&fields=id,name,job_title,email" \
-H "Authorization: Bearer $ENTRYBIT_API_KEY"{
"success": true,
"items": [
{ "id": "emp_7b2d1ac98ef04c35", "name": "Dana Cohen", "job_title": "Facilities Manager", "email": "dana@example.com" }
],
"total": 87,
"next_cursor": "eyJpZCI6…",
"has_more": true
}Get a member
GET /api/v1/org/members/{public_id}Scope: org:members:read. One member by public id (emp_…) — 404 when no such member exists in the organization. Deactivated members are returned here (with "status": "inactive") — their id stays addressable, Directory-style. Accepts the same fields parameter.
{ "success": true, "member": { "id": "emp_7b2d1ac98ef04c35", "name": "Dana Cohen", "first_name": "Dana", "last_name": "Cohen", "job_title": "Facilities Manager", "department": "Operations", "status": "active", "created_at": "2026-05-19T09:30:00" } }The Member object
| Field | Tier | Type | Notes |
|---|---|---|---|
id | basic | string | Public identifier, e.g. emp_7b2d1ac98ef04c35. |
name | basic | string | Display name (first + last). |
first_name | basic | string | |
last_name | basic | string | |
job_title | basic | string | null | |
department | basic | string | null | The department’s display name. |
status | basic | string | active or inactive. |
created_at | basic | string | null | |
email | contact | string | null | Requires org:members:contact:read. |
phone | contact | string | null | Requires org:members:contact:read. |
Contact fields are stored encrypted at rest and decrypted server-side only when the credential carries the contact scope. The API never returns credential material, photo URLs, or internal indexes.
Field selection and tiers
Graph-style sparse fieldsets: pass fields as a comma-separated subset of the table above. Omit it and you get every field your credential’s tier allows — the basic tier with org:members:read, plus the contact tier when the key also carries org:members:contact:read.
- An unknown field returns
400, and the message lists the allowed fields. - A contact field on a basic-only key returns
403 insufficient_scope, and the challenge names the missing scope:
# Key carries org:members:read only — email is a contact-tier field
curl "https://entrybit.net/api/v1/org/members?fields=id,name,email" \
-H "Authorization: Bearer $ENTRYBIT_API_KEY"HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", scope="org:members:contact:read"
{ "error": "insufficient_scope" }Signed-in access — the members:read scope
Both member endpoints also accept a user-delegated OAuth access token carrying the members:read scope — for apps a member signs into that need the directory without a server-side key. The call is then double-gated: the token must carry the scope and the signed-in user must still hold the organization’s employee-view role — a removed user cannot keep reading the directory through an old grant. The response is scoped to the user’s own organization, and members:read unlocks the full field set (the user’s role already grants the in-app member list).
Scope summary
| Endpoint | Scope |
|---|---|
GET /api/v1/org/passes | org:passes:read |
POST /api/v1/org/passes | org:passes:write |
DELETE /api/v1/org/passes/{public_id} | org:passes:write |
GET /api/v1/org/facilities | org:facilities:read |
GET /api/v1/org/members | org:members:read (+ org:members:contact:read for contact fields) |
GET /api/v1/org/members/{public_id} | org:members:read (+ org:members:contact:read for contact fields) |
Auth failures (401/403/429) follow the API-key challenge rules; business errors follow the error catalog.