openapi: 3.1.0
info:
  title: HeyPeppy B2B Enterprise API
  version: 1.0.0
  description: |
    # HeyPeppy Multi-Tenant AI Receipt Processor & Submission Auditor API
    
    Enterprise API for **Education Savings Account (ESA) administrators**, **scholarship funding organizations (SFOs)**, and **state educational choice programs**.
    Enables automated, AI-powered receipt extraction, proof-of-payment auditing, and reimbursement claim compliance verification across customizable program guidelines.
    
    ## Core Capabilities
    - **Multi-Tenant Architecture**: Isolate jobs, data retention rules, webhooks, and billing per B2B tenant via `X-Tenant-ID` or API key context.
    - **Vision AI Receipt Processing**: Extracts itemized line items, merchant details, totals, and proof of payment from PDFs and images.
    - **Customizable Policy Auditor**: Evaluates reimbursement packages against scholarship purchasing guidelines, handbook rules, and custom category lists (e.g., Florida PEP/UA, Arizona ESA, and custom state rules).
    - **High-Throughput Work Queue**: Serverless asynchronous pipeline powered by Google Cloud Tasks for massive burst scaling and automatic retries.
    - **Real-Time Job Inspection & Webhooks**: Polling and HMAC-signed webhook delivery for asynchronous job lifecycle events.
  contact:
    name: HeyPeppy API Support
    email: api-support@heypeppy.com
    url: https://heypeppy.com

servers:
  - url: https://api.heypeppy.ai/v1
    description: Production API Server
  - url: https://staging-api.heypeppy.com/v1
    description: Sandbox / Staging Server

security:
  - ApiKeyAuth: []

paths:
  /receipts/extract:
    post:
      summary: Submit Receipt for Asynchronous Extraction
      description: |
        Uploads or references a receipt image/PDF for AI extraction.
        Enqueues the job into the tenant's work queue and immediately returns a `202 Accepted` response with a `jobId`.
      operationId: extractReceiptAsync
      parameters:
        - $ref: '#/components/parameters/TenantIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReceiptExtractionRequest'
      responses:
        '202':
          description: Job successfully enqueued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobQueuedResponse'
        '400':
          $ref: '#/components/responses/400BadRequest'
        '401':
          $ref: '#/components/responses/401Unauthorized'
        '429':
          $ref: '#/components/responses/429RateLimited'

  /receipts/extract-sync:
    post:
      summary: Synchronous Receipt Extraction (Low-Latency)
      description: |
        Extracts receipt data synchronously. Recommended for interactive single-page receipts
        where immediate low latency (<8s) is preferred over queuing.
      operationId: extractReceiptSync
      parameters:
        - $ref: '#/components/parameters/TenantIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReceiptExtractionRequest'
      responses:
        '200':
          description: Extraction complete
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReceiptExtractionResult'
        '400':
          $ref: '#/components/responses/400BadRequest'
        '401':
          $ref: '#/components/responses/401Unauthorized'
        '504':
          description: Gateway Timeout (job took longer than synchronous cutoff; retry via asynchronous endpoint)

  /submissions/audit:
    post:
      summary: Audit Reimbursement Claim Package
      description: |
        Evaluates a reimbursement claim against the specified scholarship program purchasing guidelines and handbook rules.
        Performs proof-of-payment cross-referencing, purchase limitation checks, mandatory supporting documentation detection, and educational justification strength scoring.
      operationId: auditSubmission
      parameters:
        - $ref: '#/components/parameters/TenantIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmissionAuditRequest'
      responses:
        '202':
          description: Audit job enqueued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobQueuedResponse'
        '200':
          description: Audit evaluated immediately (when async is false)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditResult'
        '400':
          $ref: '#/components/responses/400BadRequest'
        '401':
          $ref: '#/components/responses/401Unauthorized'

  /batches:
    post:
      summary: Create Bulk Batch Job
      description: |
        Submits up to 1,000 reimbursement claims or receipts in a single batch call.
        Individual tasks are distributed across the Cloud Tasks queue with concurrency management and tenant isolation.
      operationId: createBatch
      parameters:
        - $ref: '#/components/parameters/TenantIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - batchType
                - items
              properties:
                tenantId:
                  type: string
                  example: "stepup"
                batchType:
                  type: string
                  enum: [receipt_extraction, submission_audit]
                webhookUrl:
                  type: string
                  format: uri
                items:
                  type: array
                  items:
                    type: object
      responses:
        '202':
          description: Batch accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  batchId:
                    type: string
                    example: "batch_987xyz"
                  totalItems:
                    type: integer
                    example: 250
                  status:
                    type: string
                    example: "processing"
                  createdAt:
                    type: string
                    format: date-time

  /jobs/{jobId}:
    get:
      summary: Get Job Status & Results
      description: Retrieves the current execution state, timing, and results of an asynchronous job.
      operationId: getJobStatus
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
          example: "job_01HXYZ1234"
        - $ref: '#/components/parameters/TenantIdHeader'
      responses:
        '200':
          description: Job status details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatusResponse'
        '404':
          description: Job not found

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: 'Format: Bearer <API_KEY>'

  parameters:
    TenantIdHeader:
      name: X-Tenant-ID
      in: header
      required: false
      description: B2B Tenant identifier (e.g., 'stepup', 'classwallet', 'odyssey'). If omitted, the tenant is inferred from your API key.
      schema:
        type: string
        example: "stepup"

  responses:
    400BadRequest:
      description: Invalid request payload or missing parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    401Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    429RateLimited:
      description: Concurrency limit or rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

  schemas:
    ReceiptExtractionRequest:
      type: object
      required:
        - fileUrl
      properties:
        tenantId:
          type: string
          description: Optional B2B tenant ID (can also be passed via X-Tenant-ID header).
          example: "stepup"
        fileUrl:
          type: string
          format: uri
          description: Public or pre-signed URL to the PDF or image file.
          example: "https://storage.googleapis.com/partner-uploads/receipt_1029.pdf"
        fileName:
          type: string
          example: "receipt_1029.pdf"
        fileType:
          type: string
          example: "application/pdf"
        webhookUrl:
          type: string
          format: uri
          description: Optional endpoint to receive a POST webhook on job completion.
          example: "https://portal.partner.org/api/webhooks/peppy"
        studentCandidates:
          type: array
          description: List of registered students in the family/account to automatically map student names.
          items:
            $ref: '#/components/schemas/StudentCandidate'
        categorizeItems:
          type: boolean
          default: false
          description: Whether to execute purchasing policy audit & guideline categorization on extracted line items. When false (default), fast OCR text extraction and parsing is performed.
          example: false
        programKey:
          type: string
          enum: [PEP, UA, FTC-FES-EO]
          default: PEP
          description: Target scholarship program guideline rules to evaluate against when categorizeItems is true. Inferred from matched candidate if omitted.
          example: "PEP"
        metadata:
          type: object
          description: Passthrough metadata preserved across job lifecycle, webhooks, and reporting.
          additionalProperties: true

    StudentCandidate:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: string
          example: "stu_1002"
        name:
          type: string
          example: "Emma Davis"
        officialName:
          type: string
          example: "Emma Grace Davis"
        gradeLevel:
          type: string
          example: "5th Grade"
        scholarshipProgram:
          type: string
          example: "PEP"

    JobQueuedResponse:
      type: object
      properties:
        jobId:
          type: string
          example: "job_01HXYZ1234"
        tenantId:
          type: string
          example: "stepup"
        status:
          type: string
          enum: [queued, processing]
          example: "queued"
        pollUrl:
          type: string
          example: "https://api.heypeppy.com/v1/jobs/job_01HXYZ1234"
        estimatedWaitMs:
          type: integer
          example: 6000

    JobStatusResponse:
      type: object
      properties:
        jobId:
          type: string
          example: "job_01HXYZ1234"
        tenantId:
          type: string
          example: "stepup"
        status:
          type: string
          enum: [queued, processing, completed, failed]
          example: "completed"
        attempts:
          type: integer
          example: 1
        timing:
          type: object
          properties:
            queuedAt:
              type: string
              format: date-time
            startedAt:
              type: string
              format: date-time
            completedAt:
              type: string
              format: date-time
            durationMs:
              type: integer
              example: 4120
        result:
          oneOf:
            - $ref: '#/components/schemas/ReceiptExtractionResult'
            - $ref: '#/components/schemas/AuditResult'
        error:
          type: string
          nullable: true

    ReceiptExtractionResult:
      type: object
      properties:
        vendor:
          type: string
          nullable: true
          example: "Kumon Math & Reading Center"
        amount:
          type: number
          nullable: true
          example: 180.00
        transactionDate:
          type: string
          nullable: true
          example: "2026-09-02"
        invoiceType:
          type: string
          enum: [retail, service]
          example: "service"
        items:
          type: array
          items:
            $ref: '#/components/schemas/ExtractedLineItem'
        paymentDetails:
          $ref: '#/components/schemas/PaymentProofAudit'
        studentDetails:
          $ref: '#/components/schemas/StudentMatchDetails'
        serviceDetails:
          $ref: '#/components/schemas/ExtractedServiceDetails'
          nullable: true
        confidence:
          type: integer
          example: 95
        auditResult:
          $ref: '#/components/schemas/ReceiptCompletenessAudit'

    ExtractedServiceDetails:
      type: object
      properties:
        isService:
          type: boolean
          example: true
        providerName:
          type: string
          nullable: true
          example: "Lisa Schonauer"
        servicePeriod:
          type: string
          nullable: true
          example: "May 2026"
        serviceRate:
          type: number
          nullable: true
          example: 54.00
        serviceType:
          type: string
          nullable: true
          example: "Music Lessons"

    ExtractedLineItem:
      type: object
      properties:
        description:
          type: string
          example: "Monthly Math Tutoring - September 2026"
        price:
          type: number
          example: 180.00
        quantity:
          type: integer
          example: 1
        salesTax:
          type: number
          nullable: true
          example: 0
        category:
          type: string
          description: Standardized Level-1 reimbursement category (when categorizeItems is true).
          example: "Instruction & Tutoring"
        type:
          type: string
          description: Standardized Level-2 subcategory.
          example: "Tutoring Services"
        detail:
          type: string
          description: Standardized Level-3 detail categorization.
          example: "Mathematics Tutoring"
        confidence:
          type: integer
          description: AI classification confidence percentage (0-100).
          example: 95
        isEligible:
          type: boolean
          description: Program eligibility verdict according to state guidelines.
          example: true
        justification:
          type: string
          description: AI policy justification for reimbursement decision.
          example: "Tutoring in core academic subjects is eligible under PEP guidelines."
        eligibilityNotes:
          type: string
          description: Specific compliance or documentation notes for claim approval.
          example: "Requires certified tutor credentials or formal center invoice."
        reason:
          type: string
          description: Contextual explanation for the determination.
          example: "Direct academic instruction."
        warnings:
          type: array
          items:
            type: string
          description: Potential warning flags or documentation caveats.
          example: []

    PaymentProofAudit:
      type: object
      properties:
        isPaid:
          type: boolean
          example: true
        paymentMethod:
          type: string
          nullable: true
          example: "Visa ending in 4128"
        cardLast4:
          type: string
          nullable: true
          example: "4128"
        proofOfPaymentType:
          type: string
          enum: [card, cash, bank_transfer, check, digital_wallet, unpaid, unknown]
          example: "card"

    StudentMatchDetails:
      type: object
      properties:
        detectedStudentName:
          type: string
          nullable: true
          example: "Emma Davis"
        matchedChildId:
          type: string
          nullable: true
          example: "stu_1002"
        matchedChildName:
          type: string
          nullable: true
          example: "Emma Grace Davis"
        matchConfidence:
          type: integer
          example: 95

    ReceiptCompletenessAudit:
      type: object
      properties:
        summary:
          type: string
          example: "Document contains valid proof of payment."
        isServiceBased:
          type: boolean
          example: true
        isUnpaidInvoice:
          type: boolean
          example: false
        hasLineItems:
          type: boolean
          example: true
        hasTransactionDate:
          type: boolean
          example: true
        hasProviderName:
          type: boolean
          example: true
        hasTotalPrice:
          type: boolean
          example: true
        hasPaidIndication:
          type: boolean
          example: true

    SubmissionAuditRequest:
      type: object
      required:
        - vendor
        - amount
        - lineItems
      properties:
        tenantId:
          type: string
          description: Optional B2B tenant identifier.
          example: "stepup"
        vendor:
          type: string
          example: "Florida Virtual School"
        amount:
          type: number
          example: 325.00
        programKey:
          type: string
          description: Identifier for the scholarship program or policy rule set (e.g. FL_PEP, FL_UA, AZ_ESA, or tenant-specific program key).
          default: "FL_PEP"
          example: "FL_PEP"
        lineItems:
          type: array
          items:
            $ref: '#/components/schemas/AuditLineItem'
        supportingFiles:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
                example: "course_syllabus.pdf"
        links:
          type: array
          items:
            type: object
            properties:
              url:
                type: string
                example: "https://flvs.net/courses/algebra1"
              label:
                type: string
                example: "Course Scope & Sequence"
        async:
          type: boolean
          default: true
        webhookUrl:
          type: string
          format: uri

    AuditLineItem:
      type: object
      required:
        - description
        - price
      properties:
        description:
          type: string
          example: "Algebra 1 Honors Full Curriculum Course"
        price:
          type: number
          example: 325.00
        category:
          type: string
          example: "Curriculum/Course"
        type:
          type: string
          example: "Online"
        detail:
          type: string
          example: "High School"
        justification:
          type: string
          example: "Full year 9th grade high school math curriculum for student Emma to satisfy academic course requirements."

    AuditResult:
      type: object
      properties:
        confidenceScore:
          type: integer
          example: 92
        overallStatus:
          type: string
          enum: [pass, review, fail]
          example: "pass"
        summary:
          type: string
          example: "This submission meets all core program requirements and is ready to submit."
        receiptFormat:
          type: object
          properties:
            isValid:
              type: boolean
              example: true
            summary:
              type: string
              example: "All required receipt format elements are present."
            missingFields:
              type: array
              items:
                type: string
        purchaseLimitations:
          type: object
          properties:
            hasLimitations:
              type: boolean
              example: false
            summary:
              type: string
              example: "No purchase frequency or dollar limits apply to these items."
            limitations:
              type: array
              items:
                type: object
        supportingDocumentation:
          type: object
          properties:
            hasRequirements:
              type: boolean
              example: true
            summary:
              type: string
              example: "All required supporting documentation is attached/linked."
            requiredDocuments:
              type: array
              items:
                type: object
                properties:
                  documentType:
                    type: string
                    example: "Curriculum Syllabus"
                  isAttached:
                    type: boolean
                    example: true
                  status:
                    type: string
                    example: "attached"
        flags:
          type: array
          items:
            type: object
            properties:
              severity:
                type: string
                enum: [error, warning, info]
              field:
                type: string
              message:
                type: string
              suggestion:
                type: string
        justificationAnalysis:
          type: array
          items:
            type: object
            properties:
              lineItemIndex:
                type: integer
                example: 0
              strength:
                type: string
                enum: [strong, adequate, weak]
                example: "strong"
              reason:
                type: string
                example: "Justification clearly articulates the educational purpose and benefit for the student."
              improvementHints:
                type: array
                items:
                  type: string
        disclaimer:
          type: string
          example: "This audit is an AI-powered review and does not guarantee reimbursement approval or denial. Program administrators make all final determinations on reimbursement eligibility."
        auditRunAt:
          type: string
          format: date-time

    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: "UNAUTHORIZED"
            message:
              type: string
              example: "Invalid API key provided."
            status:
              type: integer
              example: 401
