Skip to content

PINs API

PINs provide anonymous, shared-device access. Each project can have up to 10 active PINs.

GET /admin/projects/:projectId/pins
Authorization: Bearer <admin_token>
{
"pins": [
{
"id": "pin_abc123",
"label": "Living room TV",
"status": "active",
"privileges": ["view", "date-spots"],
"created_at": "2025-04-01T12:00:00Z",
"revoked_at": null
},
{
"id": "pin_def456",
"label": "Old tablet",
"status": "revoked",
"privileges": ["view"],
"created_at": "2025-02-15T09:00:00Z",
"revoked_at": "2025-05-01T10:00:00Z"
}
]
}
POST /admin/projects/:projectId/pins
Authorization: Bearer <admin_token>
Content-Type: application/json
FieldTypeRequiredDescription
pinstringYesNumeric PIN (minimum 5 digits)
labelstringYesHuman-readable label
privilegesstring[]NoArray of privilege strings (default: [])
Terminal window
curl -X POST https://auth.beshoy.ai/admin/projects/proj_trip/pins \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pin": "84291",
"label": "Bedroom tablet",
"privileges": ["view", "edit", "date-spots"]
}'
{
"id": "pin_xyz789"
}
  • PIN must be at least 5 digits (numeric only)
  • Maximum 10 active PINs per project
  • Label is required (helps identify which device/user)
PATCH /admin/projects/:projectId/pins/:pinId
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"status": "revoked"
}
{ "ok": true }
  • Immediate: new PIN authentication attempts with this PIN are rejected
  • On next refresh: existing sessions using this PIN receive 403 PIN revoked
  • Within 5 min: current access tokens expire naturally

Privileges are arbitrary strings that your app interprets. The auth service embeds them in the PIN token but doesn’t enforce them. Common patterns:

["view"] // Read-only access
["view", "edit"] // Full access
["view", "date-spots"] // Read + specific feature access

Your app checks these in middleware or route handlers:

const { payload } = await jwtVerify(token, secret);
const privileges: string[] = payload.privileges || [];
if (!privileges.includes('edit')) {
return new Response('Forbidden', { status: 403 });
}