> ## Documentation Index
> Fetch the complete documentation index at: https://developers.nexspace365.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Get webhook subscription with recent deliveries

> Retrieve a webhook subscription and its recent delivery attempts. **Required API-key scope:** `integrations:write`



## OpenAPI

````yaml https://api.nexspace365.com/api/openapi.json get /api/webhooks/{id}
openapi: 3.0.3
info:
  title: NexSpace 365 API
  description: >
    Multi-industry enterprise workforce management platform — scheduling, HR,

    payroll, CRM, AI, and messaging suites.


    ## Authentication

    Three schemes: session cookie, JWT bearer, or API key (`nex_live_…` /

    `nex_test_…` / `nex_pat_…`). AI agents should use API keys with appropriate

    scopes.


    ## Authorization

    Endpoints require specific permissions via the RBAC system:

    - **Internal team**: Full platform access

    - **Facility users**: Scoped to their org/facilities

    - **Staff**: Limited to own data


    ## Error Contract

    All errors return `{ error: { message, code?, suggestion?, retryable? },
    requestId? }`.

    The `suggestion` field hints at how to fix the request; `retryable` signals
    whether

    retry-with-backoff is appropriate.


    ## Versioning

    Pin your integration to a specific API version via the `NexSpace-Version`
    header

    (date-based, e.g. `2026-05-10`). When absent, the latest version is assumed.

    Deprecated versions include `Sunset` and `Deprecation` headers. Discovery at

    `GET /.well-known/api-versions`.


    ## Idempotency

    Write operations (POST/PUT/PATCH) accept an `Idempotency-Key` header. If

    the same key + body combination is sent again within 24 hours, the original

    response is replayed. Different body with the same key returns 409 Conflict.


    ## MCP (Model Context Protocol)

    AI agents interact via `POST /mcp` using JSON-RPC 2.0. Call `tools/list` to

    discover available verbs, then `tools/call` to invoke them.
  version: 1.0.0
  contact:
    name: NexSpace API Support
    email: support@nexspace365.com
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
servers:
  - url: https://api.nexspace365.com
    description: Production server
security:
  - bearerAuth: []
  - apiKeyAuth: []
tags:
  - name: Authentication
    description: User authentication and session management
  - name: Facilities
    description: Facility management
  - name: Staff
    description: Staff member management and profiles
  - name: Credentials
    description: Staff credential tracking — licenses, certifications, and training
  - name: Shifts
    description: Shift scheduling and management
  - name: Dashboard
    description: Dashboard statistics and widgets
  - name: API Keys
    description: API key lifecycle — create, list, revoke, rotate
  - name: MCP
    description: Model Context Protocol endpoint for AI agent tool calling
  - name: OAuth
    description: OAuth 2.1 authorization server — DCR, authorize, token
  - name: Webhooks
    description: Outbound webhook subscription management
  - name: Analytics
    description: API usage analytics and observability
paths:
  /api/webhooks/{id}:
    get:
      tags:
        - Webhooks
      summary: Get webhook subscription with recent deliveries
      description: >-
        Retrieve a webhook subscription and its recent delivery attempts.
        **Required API-key scope:** `integrations:write`
      operationId: getWebhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - $ref: '#/components/parameters/OrgUnitIdQuery'
      responses:
        '200':
          description: Subscription detail + last 25 delivery attempts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSubscriptionDetailResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/InsufficientScope'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - bearerAuth: []
        - apiKeyAuth: []
components:
  parameters:
    OrgUnitIdQuery:
      name: orgUnitId
      in: query
      required: false
      schema:
        type: integer
      description: >
        Target organization unit (team) id. Internal operators with cross-org
        access use this for webhook CRUD; facility-scoped sessions omit it and
        the org is resolved from context.
  schemas:
    WebhookSubscriptionDetailResponse:
      type: object
      properties:
        subscription:
          $ref: '#/components/schemas/WebhookSubscription'
        deliveries:
          type: array
          items:
            $ref: '#/components/schemas/WebhookDelivery'
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            message:
              type: string
              description: Human-readable error message
            code:
              type: string
              description: Machine-readable error code (e.g. VALIDATION_FAILED, NOT_FOUND)
            details:
              type: object
              description: Structured details (e.g. Zod validation issues)
            suggestion:
              type: string
              description: Hint for agents on how to recover
            retryable:
              type: boolean
              description: Whether the same request might succeed on retry
          required:
            - message
        requestId:
          type: string
          description: Correlation ID for logs and Sentry
      required:
        - error
    WebhookSubscription:
      type: object
      properties:
        id:
          type: integer
        orgId:
          type: integer
        name:
          type: string
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        isActive:
          type: boolean
        facilityId:
          type: integer
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
      example:
        id: 3
        orgId: 1
        name: Ops Slack bridge
        url: https://hooks.example.com/nexspace
        events:
          - shift_filled
          - credential_expired
        isActive: true
        facilityId: 1
        createdAt: '2026-04-01T10:00:00Z'
        updatedAt: '2026-05-01T10:00:00Z'
    WebhookDelivery:
      type: object
      properties:
        id:
          type: integer
        subscriptionId:
          type: integer
        eventType:
          type: string
        payload:
          allOf:
            - $ref: '#/components/schemas/WebhookEvent'
          description: The signed wire envelope as stored at delivery time.
        attempt:
          type: integer
        statusCode:
          type: integer
          nullable: true
        responseBody:
          type: string
          nullable: true
        error:
          type: string
          nullable: true
        deliveredAt:
          type: string
          format: date-time
          nullable: true
        nextRetryAt:
          type: string
          format: date-time
          nullable: true
        createdAt:
          type: string
          format: date-time
    WebhookEvent:
      type: object
      description: >
        The signed JSON envelope POSTed to a subscription's `url` when a
        subscribed event fires. Delivery headers: `X-NexSpace-Signature:
        sha256=<hmac>` (HMAC-SHA256 of the raw body with the subscription's
        signing secret), `X-NexSpace-Event: <type>`, `X-NexSpace-Retry: <n>`
        (present only on retries), and `User-Agent: NexSpace-Webhooks/1.0`.
        Receivers should treat `id` as an idempotency key. The `data` object's
        shape depends on `type`:
          - `shift_posted` — `{ shiftId, facilityId, status }`
          - `shift_filled` — `{ shiftId, facilityId, assignedStaffIds }`
          - `timesheet_approved` — `{ shiftWorkLogId, shiftId, staffId, facilityId }`
          - `payroll_completed` — `{ payrollRunId, facilityId, externalPayrollId, status }`
          - `staff_onboarded` — `{ staffId, facilityId }`
          - `lead_qualified` — `{ leadId, orgUnitId }`
          - `credential_expired` — `{ credentialId, userId, staffId, name, type, facilityId }`
          - `agent_run.pending_approval` — `{ runId, agentId, status, facilityId, sessionId, pendingApprovalIds }`
          - `agent_run.completed` — `{ runId, agentId, status, facilityId, sessionId, toolsInvoked }`
          - `agent_run.failed` — `{ runId, agentId, status, facilityId, sessionId, error }`
          - `test` — arbitrary payload from the "send test event" endpoint
      required:
        - id
        - type
        - data
        - occurredAt
      properties:
        id:
          type: string
          format: uuid
          description: Unique delivery id — use as an idempotency key on the receiver.
        type:
          type: string
          description: Event name.
          enum:
            - shift_posted
            - shift_filled
            - timesheet_approved
            - payroll_completed
            - staff_onboarded
            - lead_qualified
            - credential_expired
            - agent_run.pending_approval
            - agent_run.completed
            - agent_run.failed
            - test
        data:
          type: object
          additionalProperties: true
          description: Event-specific body; shape depends on `type` (see above).
        occurredAt:
          type: string
          format: date-time
          description: When the event fired (ISO 8601, UTC).
      example:
        id: 6f1c2f7a-9b3e-4c2a-8d1f-2a5b7c9e0d11
        type: shift_filled
        data:
          shiftId: 1024
          facilityId: 1
          assignedStaffIds:
            - 42
            - 58
        occurredAt: '2026-05-20T16:00:05Z'
  responses:
    UnauthorizedError:
      description: Missing, invalid, expired, or revoked credential.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              message: Invalid or revoked API key
              code: UNAUTHENTICATED
              suggestion: >-
                Send a valid `Authorization: Bearer <token>` (nex_live_/nex_pat_
                key, JWT, or OAuth access token).
              retryable: false
    InsufficientScope:
      description: >
        Authenticated, but the API key's granted scopes do not include the scope
        this operation requires (see the operation's "Required API-key scope").
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              message: Insufficient API key scope
              code: INSUFFICIENT_SCOPE
              suggestion: >-
                Mint or rotate a key that includes the required scope, or grant
                a wildcard like `resource:*`.
              retryable: false
    RateLimited:
      description: >
        Per-key rate limit exceeded. Includes `Retry-After` and the full
        `X-RateLimit-*` set so agents can back off precisely (NEX-1659).
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              message: Rate limit exceeded for this API key
              code: API_KEY_RATE_LIMITED
              suggestion: >-
                Wait until X-RateLimit-Reset before retrying, or batch
                operations.
              retryable: true
  headers:
    Retry-After:
      description: Seconds to wait before retrying (present on 429).
      schema:
        type: integer
    X-RateLimit-Limit:
      description: Requests allowed per window for this API key (NEX-1659).
      schema:
        type: integer
    X-RateLimit-Remaining:
      description: Requests remaining in the current window.
      schema:
        type: integer
    X-RateLimit-Reset:
      description: Unix timestamp (seconds) when the current window resets.
      schema:
        type: integer
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token authentication
    apiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: NexSpaceApiKey
      description: |
        API key or personal access token (NEX-1042). Send as
        `Authorization: Bearer <token>`. Token format:
          - `nex_live_…`  — live API key (server-to-server)
          - `nex_test_…`  — sandbox API key
          - `nex_pat_…`   — personal access token (acts as the issuing user)

        Authorization is decided by the key's `scopes` array, not the
        owning user's RBAC role. See `POST /api/api-keys` to mint a key.

````