openapi: 3.0.3
info:
  title: EmployJoy Partner API
  version: v1
  description: |
    Partner API for ATS integrations with EmployJoy's hiring pipeline.
    Submit candidates, discover jobs, and receive real-time webhooks.
  contact:
    email: partnerships@employjoy.ai
    url: https://docs.employjoy.ai
servers:
  - url: https://api.employjoy.ai/api/v1/external
    description: Production
security:
  - bearerAuth: []

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: "Partner API key (pek_live_* or pek_test_*)"

  schemas:
    Job:
      type: object
      properties:
        id: { type: string, example: "job_12345" }
        title: { type: string, example: "Janitor" }
        company:
          type: object
          properties:
            id: { type: string, example: "co_67" }
            name: { type: string, example: "Acme Cleaning" }
        location:
          type: object
          properties:
            city: { type: string, example: "Tampa" }
            state: { type: string, example: "FL" }
            country: { type: string, example: "US" }
        compensation:
          type: object
          properties:
            type: { type: string, example: "hourly" }
            min: { type: number, nullable: true, example: 18 }
            max: { type: number, nullable: true, example: 22 }
            currency: { type: string, example: "USD" }
        schedule: { type: string, nullable: true, example: "full_time" }
        status: { type: string, enum: [open], example: "open" }
        headcount:
          type: object
          properties:
            total: { type: integer, nullable: true }
            filled: { type: integer, example: 2 }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
        applicationFormSchema:
          type: array
          nullable: true
          items:
            type: object
            properties:
              key: { type: string }
              label: { type: string }
              type: { type: string }
              required: { type: boolean }

    Application:
      type: object
      properties:
        id: { type: string, example: "ej_app_789" }
        sourceApplicationId: { type: string, nullable: true }
        jobId: { type: string, example: "job_12345" }
        status: { type: string, enum: [received, in_progress, accepted, rejected] }
        currentStage: { type: string, example: "Video Interview" }
        submittedAt: { type: string, format: date-time }
        lastStatusChangeAt: { type: string, format: date-time }

    Error:
      type: object
      required: [error, message]
      properties:
        error: { type: string }
        message: { type: string }
        details: { type: object }

    WebhookEvent:
      type: object
      properties:
        id: { type: string, example: "evt_2026052214301198abc" }
        type: { type: string, enum: [application.status_changed, job.opened, job.closed] }
        createdAt: { type: string, format: date-time }
        apiVersion: { type: string, example: "v1" }
        data: { type: object }

paths:
  /jobs:
    get:
      summary: List available jobs
      description: Returns open jobs at companies where your key has active access.
      parameters:
        - name: updated_since
          in: query
          schema: { type: string, format: date-time }
        - name: cursor
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
        - name: company_id
          in: query
          schema: { type: string }
        - name: status
          in: query
          schema: { type: string, enum: [open] }
      responses:
        '200':
          description: List of jobs
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/Job' }
                  next_cursor: { type: string }
        '401': { description: Unauthorized, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
        '429': { description: Rate limited }

  /applications:
    post:
      summary: Submit an application
      description: Submit a candidate to a job. Requires Idempotency-Key header.
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema: { type: string, format: uuid }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [jobId, candidate, consentToContact]
              properties:
                jobId: { type: string, example: "job_12345" }
                candidate:
                  type: object
                  required: [firstName, lastName, email, phone]
                  properties:
                    firstName: { type: string, maxLength: 100 }
                    lastName: { type: string, maxLength: 100 }
                    email: { type: string, format: email }
                    phone: { type: string, example: "+15551234567" }
                consentToContact: { type: boolean, enum: [true] }
                source: { type: string, maxLength: 100 }
                sourceApplicationId: { type: string, maxLength: 128 }
                applicationFormAnswers: { type: object }
                resumeUrl: { type: string, format: uri }
      responses:
        '201':
          description: Application created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string, example: "ej_app_789" }
                  sourceApplicationId: { type: string }
                  jobId: { type: string }
                  status: { type: string, example: "received" }
                  createdAt: { type: string, format: date-time }
        '401': { description: Unauthorized }
        '404': { description: Job not found }
        '409': { description: Job closed or duplicate sourceApplicationId }
        '422': { description: Validation error or idempotency issue }
        '429': { description: Rate limited }
    get:
      summary: List your applications
      parameters:
        - name: updated_since
          in: query
          schema: { type: string, format: date-time }
        - name: cursor
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
        - name: company_id
          in: query
          schema: { type: string }
        - name: status
          in: query
          schema: { type: string, enum: [received, in_progress, accepted, rejected] }
      responses:
        '200':
          description: List of applications
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/Application' }
                  next_cursor: { type: string }

  /applications/{id}:
    get:
      summary: Get application status
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
          example: "ej_app_789"
      responses:
        '200':
          description: Application details
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Application' }
        '404': { description: Application not found }
