Changelog
All notable changes to the Buddy Punch Public API are documented here.
2026-06-10 — Bulk Pay Rates
Added
GET /employee/payrates— returns active pay rates for every employee you can access, grouped per employee, in one paginated call. A bulk alternative to fanning outGET /employee/{id}/payratesonce per employee (the closest analog to the legacy APIMGET /employee/pay-ratesendpoint, which returned all employees' rates in a single response).- Response is the standard list envelope (
{ object: "list", data: [...], hasMore, totalCount, url }). Eachdataitem is{ employeeId, payRates: [...] }, where each pay-rate object is the same shape as the per-employeeGET /employee/{id}/payratesresponse — so you can parse rates identically whether you fetch one employee or the whole account. - Employees with no active pay rates are included with an empty
payRatesarray. - Paginated with
?limit=(default 25, max 100) and?startingAfter=/?endingBefore=(cursor is the employee id). - Requires
employees:read. Respects the same manager scoping asGET /employee— you only get rates for employees you can already access.
- Response is the standard list envelope (
2026-05-18 — Stripe-style API key prefixes
Changed
- Newly minted API keys now carry a
bp_live_prefix. Existing keys (the legacybp_<hex>shape from before this change) continue to authenticate unchanged — validation is hash-based, not prefix-based, so no migration is required. If you want a key with the new prefix, regenerate it from the dashboard. ApiKey.KeyPrefixcolumn widened fromNVARCHAR(12)toNVARCHAR(20)to fit the longer visible prefix shown in the dashboard.
2026-05-15 — Schedules, Shifts, and Time Card terminology cleanup
Added
Shifts — Full CRUD for scheduled shifts:
POST /shift— createGET /shift/{id}— get by idPUT /shift/{id}— updateDELETE /shift/{id}— delete
Each shift carries start/end times in both UTC and local-account-timezone forms, scheduled hours, optional location/job-code/position assignments, an optional employee assignment, a
publishedflag, and notes.Schedules —
GET /schedulereturns the schedule (paginated list ofShiftResourceitems) for a date range, with optional filters:locationIds,jobCodeIds,positionIds,employeeIds(each accepts repeated query-string values, e.g.?locationIds=1&locationIds=2). Mirrors the legacyapi/schedulefilter signature.New API key permission scope:
shifts:read/shifts:write(gates both the Shift CRUD endpoints andGET /schedule).
Changed (breaking)
GET /timeCard/{id}/detailsresponse shape:punchesarray renamed totimes— the array contains all three subtypes (punches, manually-entered time entries, and time off), each with atypediscriminator ("punch" | "time_entry" | "time_off"). The old field name was misleading.hasPendingPunchesboolean renamed tohasPendingTimes— same reason; pending state can apply to any of the three subtypes.- OpenAPI schema for the item shape renamed from
PunchDetailResourcetoTimeDetailResource. The schema name change also flows through to thedatapayload onpunch.*webhook events and theGET /punch/{id}response. The JSON shape is unchanged — only the schema name differs, so existing untyped consumers continue to work.
If you have a generated client tied to the old field names, regenerate from the updated OpenAPI document. If you parse the response by hand, rename your punches → times and hasPendingPunches → hasPendingTimes references.
2026-05-07 — Tiered Rate Limits & Trial Safeguards
Changed
- Rate limits are now tiered per account based on plan. The previous flat 1,000 rpm per-API-key ceiling has been split into a per-account quota that depends on subscription level:
- Trial / non-paying: 60 requests/minute
- Pro / Premium / Starter (paid non-Enterprise): 600 requests/minute
- Enterprise: 1,000 requests/minute
- A separate per-API-key DoS guard of 1,000 rpm continues to run alongside the per-account limit.
Added
- New
429 rate_limit_errorcodes:account_rate_limit_exceeded— emitted when the per-account per-minute quota is hit (paid plans)trial_resource_limit_exceeded— trial cap reached for a resource (e.g., total employees or webhooks created via the API)trial_resource_daily_limit_exceeded— daily per-resource cap reached on a trial accounttrial_daily_writes_exceeded— daily total of write requests reached on a trial account
- Dashboard-only: trial accounts may have at most one active API key. Surfaced as an inline form error in the Buddy Punch dashboard when creating a second key (not returned as an API response — there is no Public API endpoint for creating keys).
Documentation
- Getting Started, Errors, and Migration Guide updated with the tiered rate-limit story and the new error codes.
- OpenAPI description (rendered in the Scalar developer portal) now shows the three-tier per-account limit table.
2026-04-15 — Comprehensive Time Tracking
Added
- Punches — Full CRUD beyond punch in/out: list by time card, get details with breaks, add punch (with optional end time for open punches), edit punch in/out times, delete
- Breaks — New resource: list breaks for a punch, start/end real-time breaks, add/edit/delete manual breaks, remove automatic break deductions
- Time Cards — New
GET /timeCard/{id}/detailsendpoint returns full hour summaries (regular, overtime, double-time, time off, paid/unpaid breaks) with all punches and breaks - Time Card Times —
GET /timeCard/{id}/timesnow supports?type=filter (punch,time_entry,time_off) and returns items with atypediscriminator field - Time Off — New endpoints: list with
employeeId/startDate/endDate/includeAllStafffilters, get single, approve, delete (in addition to existing types/create/reject) - Time Entries — Full CRUD for duration-based time entries with proper 404 handling
Changed
- All client-facing "PTO" terminology renamed to "time off" (paid and unpaid time off both supported)
- Time entries documentation clarified as "duration-based" to distinguish from punches
POST /punchclarified: omitpunchOutDatetimeto create an open (in-progress) punch; supply it to record a completed punch in one callGET /timeOffis scoped to time off requests only (a single id-space for stable pagination) and honors the account's "Time Off visible to all staff" setting combined with the caller'sincludeAllStaff=trueopt-in/punch/{id}and/punch/{punchId}/break/*routes enforce their type contracts: ids for time entries or time off return 404 and must be accessed via/timeEntry/{id}or/timeOff/{id}- Cross-account id probing tightened on
/timeOff/{id}GET / approve / delete — non-account ids return 404 instead of surfacing
Documentation
- Static guide pages now render as styled HTML when opened in a browser
- Migration guide endpoint reference table expanded to cover all active endpoints
2026-04-08 — v1 Launch
Added
- Authentication — API key-based authentication with scoped permissions (
employees:read,locations:write, etc.) - Employees — List, get, create, update employees and retrieve pay rates
- Locations — Full CRUD plus employee and department code assignment
- Department Codes — Full CRUD plus employee assignment
- Positions — Full CRUD
- Groups — Full CRUD
- Geofences — Full CRUD plus employee assignment and notification settings
- Pay Periods — List pay periods, get time data, update status flags
- Time Cards — List, get, approve/reject, and retrieve time breakdowns
- Punches — Punch in and punch out
- Time Off — List time off types, create and reject time off requests
- Time Off Accruals — List balances, list entries, create accrual entries
- Webhooks — Create, manage, and monitor webhook endpoints with HMAC-SHA256 signed deliveries
- Events — Browse event log and inspect delivery attempts
- Rate Limiting — 1,000 requests per minute per API key with
X-RateLimit-*headers - Interactive Documentation — Scalar-powered developer portal with Try It functionality
- Migration Guide — Step-by-step guide for customers migrating from the legacy APIM-based API
Changed (vs. Legacy API)
- Authentication uses
Authorization: Bearer bp_live_xxxinstead of APIM subscription keys - Responses use a consistent error envelope with
type,code,message, andrequestId - List responses use cursor-based pagination with
hasMore,totalCount, andstartingAfter/endingBeforecursors - Time endpoints relocated: legacy "Time" section is now
GET /payPeriod/{id}/timesandGET /timeCard/{id}/times
Related Guides
- Getting Started — first request, envelope, pagination
- Data Model — field reference for every resource
- Errors — error codes and retry strategy
- Idempotency — safe retries for write requests
- Code Examples — curl / Node.js / Python / C# samples
- Webhooks — real-time event notifications
- Migration Guide — moving from the legacy API