HourSquare Employees API

The Employees API from HourSquare — 2 operation(s) for employees.

Operations 2

GET /api/public/v1/employees List employees #
PUT /api/public/v1/employees/{id}/external-id Set an employee's external id #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/hoursquare:hoursquare-employees-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

hoursquare-employees-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: HourSquare Public Employees API
  description: 'The HourSquare public API: record clock events and completed shifts, read time entries, and list employees for one company.


    **Authentication.** Create an API key in HourSquare (Settings → API & integrations; you see it once). Exchange it for an access token at `https://api.hoursquare.com/api/public/v1/oauth/token` (OAuth 2.0 client credentials: `grant_type=client_credentials`, the key as `client_secret` or as the HTTP Basic password). Send the token as `Authorization: Bearer `. **Tokens live 15 minutes** — cache one and request a new one when it expires or on a 401. Never put a key or a token in a URL.


    **Scopes.** A key carries the scopes it was created with; each operation below names the one it needs (`attendance.record`, `attendance.read`, `employees.read`, …). A person''s own HourSquare token may also call the operations their permissions cover. Calling without the scope returns `403 insufficient_scope`. When fetched with a token, this document lists only the operations that token may call.


    **Rate limits.** 300 requests per minute per key on these resources and 60 per minute per key on the token endpoint, sliding windows, enforced per server instance (so approximately). Every response carries `RateLimit`, `RateLimit-Policy` and `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset`; a `429 rate_limited` carries `Retry-After`.


    **Revocation.** Revoking a key stops new tokens immediately; tokens already issued keep working until they expire, so **within 15 minutes of Revoke every request with that key fails**.


    **Errors.** Every error is `application/problem+json` (RFC 9457) with a stable snake_case `error` you can branch on (`employee_not_found`, `already_clocked_in`, `no_open_session`, `session_too_long`, `overlaps_existing_entry`, `day_locked`, `validation_failed`, `idempotency_key_reused`, `time_tracking_disabled`, `insufficient_scope`, `rate_limited`, …), the numeric `code`, a human `detail`, and a `correlationId` to quote to support. `validation_failed` (422) lists the offending fields in `errors[]`.


    **Idempotency.** Send an `Idempotency-Key` header (unique per request, at most 128 characters) on writes to make retries safe: the same key replays the first response. Without it, the same employee, event type and caller within 60 seconds of an existing entry returns that entry with `200` instead of a duplicate; a later retry may see `409` instead of the original `201`.


    **Dry run.** `X-HourSquare-Dry-Run: true` on any write validates and resolves everything, writes nothing, and returns the would-be result with `dryRun: true` and status 200. This document declares the header with default `true`, so a try-it panel never writes unless you flip it; the server treats an absent header as a real write.


    **Time zones.** `occurredAt`, `clockIn` and `clockOut` are RFC 3339 timestamps with an explicit offset. They are recorded in the employee''s workplace time zone, else the company''s; when neither is configured the wall-clock you sent is kept. The response echoes `timeZone` (an IANA id or `offset`) and the server-resolved instant.


    **Versioning.** The major version is in the path (`/api/public/v1`). Within v1 only additive changes happen: new optional fields, new endpoints, new enum values (documented as "may grow"). Breaking changes get `/v2`; v1 then carries `Deprecation` and `Sunset` headers for at least six months.


    **Keep it safe.** Keys and tokens never belong in a URL, a browser page or a log; rotate a key when in doubt (the old one keeps working for the grace period you choose).'
  version: v1
servers:
- url: https://api.hoursquare.com
  description: HourSquare public API
tags:
- name: Employees
paths:
  /api/public/v1/employees:
    get:
      tags:
      - Employees
      summary: List employees
      description: 'The company''s employees (never deleted ones) with the fields an integrator needs to build its badge map:

        id, external id, name, e-mail, employment status, department and position ids. Ordered by id; pass

        `nextCursor` back as `cursor` while `hasMore` is true.


        Requires scope `employees.read` (52).'
      operationId: Employees_List
      parameters:
      - name: cursor
        in: query
        description: The `nextCursor` of the previous page.
        schema:
          type:
          - string
          - 'null'
        x-position: 1
      - name: limit
        in: query
        description: Page size, 1 to 100. Default 25.
        schema:
          type:
          - integer
          - 'null'
          format: int32
        x-position: 2
      - name: status
        in: query
        description: 'Only employees with this employment status: `active`, `inactive`, `on_leave`, `suspended`, `terminated`.'
        schema:
          type:
          - string
          - 'null'
        x-position: 3
      responses:
        '200':
          description: One page of employees.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListEmployeesResponse'
        '422':
          description: The request itself is wrong (`validation_failed`, with one `errors[]` item per field; or `idempotency_key_reused`).
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '401':
          description: The bearer token is missing, invalid or expired (`invalid_token`). Request a new token from the token endpoint.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '403':
          description: The token does not carry the required scope (`insufficient_scope`), or time tracking is switched off for the company (`time_tracking_disabled`).
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '429':
          description: 'Rate limit exceeded (`rate_limited`): 300 requests per minute per key on this surface, approximately (per pod). Honour `Retry-After` and the `RateLimit` headers.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
      security:
      - OAuth2:
        - employees.read
      - BearerToken: []
      x-required-scope: employees.read
  /api/public/v1/employees/{id}/external-id:
    put:
      tags:
      - Employees
      summary: Set an employee's external id
      description: 'Sets (or, with a null / blank value, clears) the badge / HRIS id this employee is matched by in

        `employee.externalId`. At most 64 characters, compared exactly as sent (case-sensitive), unique inside

        your company: a value another employee already carries returns `409 already_exists` naming them.

        `X-HourSquare-Dry-Run: true` checks the value without writing.


        Requires scope `attendance.record` (86).'
      operationId: Employees_SetExternalId
      parameters:
      - name: id
        in: path
        required: true
        description: The employee's HourSquare id.
        schema:
          type: string
          format: guid
        x-position: 1
      - name: X-HourSquare-Dry-Run
        in: header
        description: 'Validate without writing: every check and resolution runs, nothing is stored, and the response carries `dryRun: true` with status 200. Send `false` (or omit the header) to write for real.'
        schema:
          type: boolean
          default: true
      requestBody:
        x-name: request
        description: The external id to store.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetEmployeeExternalIdRequest'
        required: true
        x-position: 2
      responses:
        '200':
          description: The employee's id and its external id as stored.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SetEmployeeExternalIdResponse'
        '404':
          description: No such employee (or entry) in your company (`employee_not_found`, `not_found`).
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '409':
          description: 'The request is well-formed but the current state refuses it — see `error`: `already_clocked_in`, `no_open_session`, `clock_out_before_clock_in`, `session_too_long`, `overlaps_existing_entry`, `day_locked`, `already_exists`, `business_rule_violation`, `idempotency_in_flight`.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '422':
          description: The request itself is wrong (`validation_failed`, with one `errors[]` item per field; or `idempotency_key_reused`).
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '401':
          description: The bearer token is missing, invalid or expired (`invalid_token`). Request a new token from the token endpoint.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '403':
          description: The token does not carry the required scope (`insufficient_scope`), or time tracking is switched off for the company (`time_tracking_disabled`).
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '429':
          description: 'Rate limit exceeded (`rate_limited`): 300 requests per minute per key on this surface, approximately (per pod). Honour `Retry-After` and the `RateLimit` headers.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
      security:
      - OAuth2:
        - attendance.record
      - BearerToken: []
      x-required-scope: attendance.record
components:
  schemas:
    ProblemDetails:
      type: object
      description: "The wire shape. Code is the numeric ApiExceptionCodeTypes value,\n            Error the stable snake_case string integrators branch on."
      additionalProperties: false
      properties:
        type:
          type: string
        title:
          type: string
        status:
          type: integer
          format: int32
        detail:
          type:
          - string
          - 'null'
        instance:
          type:
          - string
          - 'null'
        code:
          type: integer
          format: int32
        error:
          type: string
        correlationId:
          type:
          - string
          - 'null'
        errors:
          type:
          - array
          - 'null'
          items:
            $ref: '#/components/schemas/ProblemFieldError'
    ListEmployeesResponse:
      type: object
      additionalProperties: false
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/EmployeeListItem'
        nextCursor:
          type:
          - string
          - 'null'
        hasMore:
          type: boolean
    SetEmployeeExternalIdRequest:
      type: object
      description: Body of PUT /api/public/v1/employees/{id}/external-id.
      additionalProperties: false
      properties:
        externalId:
          type:
          - string
          - 'null'
          description: The badge / HRIS id to match this employee by (at most 64 characters, case-sensitive, unique per company). Null or blank clears it.
          maxLength: 64
    ProblemFieldError:
      type: object
      description: One validation failure inside Errors.
      additionalProperties: false
      properties:
        field:
          type: string
        message:
          type: string
        code:
          type:
          - string
          - 'null'
    EmployeeListItem:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          format: guid
        externalId:
          type:
          - string
          - 'null'
        firstName:
          type:
          - string
          - 'null'
        lastName:
          type:
          - string
          - 'null'
        email:
          type: string
        status:
          type: string
        departmentId:
          type:
          - string
          - 'null'
          format: guid
        positionId:
          type:
          - string
          - 'null'
          format: guid
    SetEmployeeExternalIdResponse:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          format: guid
        externalId:
          type:
          - string
          - 'null'
        dryRun:
          type: boolean
  securitySchemes:
    OAuth2:
      type: oauth2
      description: 'Client credentials (RFC 6749 §4.4): POST your API key as `client_secret` (or as the HTTP Basic password) to the token URL and use the returned token as a Bearer. Tokens live 15 minutes. Each operation lists the scope it requires; a key carries the scopes it was created with.'
      flows:
        clientCredentials:
          tokenUrl: https://api.hoursquare.com/api/public/v1/oauth/token
          scopes:
            attendance.record: Record clock events and completed shifts; set employee external ids (permission 86)
            attendance.read: Read time entries (permission 47)
            attendance.reports: Read time reports (permission 49)
            employees.read: Read employees (id, external id, name, email, status) (permission 52)
            leave.read: Read leave balances and requests (permission 72)
    BearerToken:
      type: http
      description: 'An access token from the token endpoint (`Authorization: Bearer <access_token>`), or a person''s own HourSquare session token: a person may call the read operations their permissions cover.'
      scheme: bearer
      bearerFormat: JWT
x-generator: NSwag v14.0.0.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))