{
  "openapi": "3.1.0",
  "info": {
    "title": "Pawanax Careers and Academy interest API",
    "version": "1.0.0",
    "summary": "Read open roles, apply for a role, and register Academy interest.",
    "description": "Pawanax publishes its open roles as a public JSON feed and accepts applications through a public form API. No account is needed.\n\nVoluntary launch-phase role. Pawanax is not paying faculty or interns at this time, and we say so up front. Every role is listed with employmentType VOLUNTEER.\n\nApplications require a PDF CV (up to 10 MB) and the candidate's explicit approval (candidateConsent). An assistant or agent must show the candidate the selected role, the CV and the final answers before submitting, and must not invent experience or submit to several roles without the candidate's instruction.\n\nA human must confirm every submission. Do not impersonate a candidate, and do not submit on a candidate's behalf without their approval.",
    "contact": {
      "name": "Pawanax careers",
      "email": "careers@pawanalife.com",
      "url": "https://pawanalife.com/careers/for-agents"
    }
  },
  "servers": [
    {
      "url": "https://pawanalife.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Careers",
      "description": "Open roles and applications"
    },
    {
      "name": "Academy",
      "description": "Pawanax Academy interest (waitlist and organisations)"
    }
  ],
  "paths": {
    "/careers/feed.json": {
      "get": {
        "tags": [
          "Careers"
        ],
        "operationId": "getCareersFeed",
        "summary": "List active open roles",
        "description": "Returns active roles only. Roles are voluntary launch-phase roles. Use the slug to build the role URL: https://pawanalife.com/careers/{slug}. The applyUrl field links to the application form on that page. Check validThrough and location on the role page before applying.",
        "responses": {
          "200": {
            "description": "Active roles",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "public, s-maxage=300, stale-while-revalidate=600"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CareersFeed"
                }
              }
            }
          },
          "503": {
            "description": "Roles temporarily unavailable. This does not mean there are zero openings. Retry after the Retry-After header value.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "example": "30"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/api/careers/apply": {
      "post": {
        "tags": [
          "Careers"
        ],
        "operationId": "submitCareerApplication",
        "summary": "Submit an application for one role",
        "description": "Submit as multipart/form-data. Let the HTTP client set the multipart boundary; do not send JSON.\n\nRequired: jobId, jobSlug, jobTitle, firstName, lastName, email, coverLetter (20 to 10,000 characters), cv (a nonempty PDF up to 10 MB), and candidateConsent (must be true).\n\nOnly report success after a 201 response. Keep application.id as the reference. If the outcome is unknown (connection lost, timeout), contact careers@pawanalife.com before resubmitting. Do not retry automatically with an unknown outcome.\n\nAcademy roles also require the academy-only fields listed in the schema.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ApplyForm"
              },
              "encoding": {
                "cv": {
                  "contentType": "application/pdf"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Submitted. Use application.id as the reference.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplySuccess"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or missing fields, a non-PDF or empty CV, a role that is no longer accepting applications, or a profile link that is not http(s).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed. Use POST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "409": {
            "description": "An application for this position already exists for this candidate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "413": {
            "description": "The attachment is too large (over 10 MB).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "500": {
            "description": "The server could not complete the request. Do not resubmit automatically; contact careers@pawanalife.com.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "x-note": "candidateConsent is REQUIRED (const true) in this document. The apply endpoint is being updated by a later change to enforce it server-side; until that ships, the server may accept submissions without it. Send it on every submission anyway, and only when the candidate has approved this submission."
      }
    },
    "/api/academy/interest": {
      "post": {
        "tags": [
          "Academy"
        ],
        "operationId": "submitAcademyInterest",
        "summary": "Join the Academy waitlist or register an organisation's interest",
        "description": "Send a JSON body with type set to waitlist or organisation. The hidden website field is a honeypot: leave it empty. Requests are rate limited per client (8 per 10 minutes). Academy pricing is not part of this API; read the Academy pages for current offers.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/AcademyWaitlist"
                  },
                  {
                    "$ref": "#/components/schemas/AcademyOrganisation"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Received. Same shape for a new and an already-listed email.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcademyInterestSuccess"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, field, or choice",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed. Use POST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait a few minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ErrorBody": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "success": {
            "type": "boolean"
          },
          "careersUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "PublicJob": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "title",
          "employmentType",
          "commitment",
          "url",
          "applyUrl"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "department": {
            "type": "string"
          },
          "location": {
            "type": "string",
            "description": "Check the role page for location terms."
          },
          "employmentType": {
            "type": "string",
            "const": "VOLUNTEER"
          },
          "roleType": {
            "type": "string",
            "description": "Internal category such as internship or full-time."
          },
          "commitment": {
            "type": "string",
            "const": "Voluntary · not paid at this time"
          },
          "description": {
            "type": "string"
          },
          "responsibilities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "requirements": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "niceToHave": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "skills": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "perks": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Non-monetary benefits only."
          },
          "datePosted": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "applyUrl": {
            "type": "string",
            "format": "uri"
          }
        },
        "additionalProperties": false
      },
      "CareersFeed": {
        "type": "object",
        "required": [
          "version",
          "organization",
          "careersUrl",
          "guideUrl",
          "fetchedAt",
          "total",
          "jobs",
          "application"
        ],
        "properties": {
          "version": {
            "type": "integer",
            "const": 1
          },
          "organization": {
            "type": "string",
            "const": "Pawanax"
          },
          "careersUrl": {
            "type": "string",
            "format": "uri"
          },
          "guideUrl": {
            "type": "string",
            "format": "uri"
          },
          "openapiUrl": {
            "type": "string",
            "format": "uri"
          },
          "commitment": {
            "type": "string",
            "const": "Voluntary · not paid at this time"
          },
          "fetchedAt": {
            "type": "string",
            "format": "date-time"
          },
          "total": {
            "type": "integer",
            "minimum": 0
          },
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicJob"
            }
          },
          "application": {
            "type": "object",
            "properties": {
              "method": {
                "type": "string",
                "const": "POST"
              },
              "endpoint": {
                "type": "string",
                "format": "uri"
              },
              "contentType": {
                "type": "string",
                "const": "multipart/form-data"
              },
              "requiredFields": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "optionalFields": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "cv": {
                "type": "object",
                "properties": {
                  "mimeType": {
                    "type": "string",
                    "const": "application/pdf"
                  },
                  "maxBytes": {
                    "type": "integer",
                    "const": 10485760
                  }
                }
              },
              "consent": {
                "type": "object",
                "properties": {
                  "field": {
                    "type": "string",
                    "const": "candidateConsent"
                  },
                  "type": {
                    "type": "string",
                    "const": "boolean"
                  },
                  "mustBe": {
                    "type": "boolean",
                    "const": true
                  },
                  "meaning": {
                    "type": "string"
                  }
                }
              },
              "accountRequired": {
                "type": "boolean",
                "const": false
              },
              "fee": {
                "type": "integer",
                "const": 0
              },
              "instructions": {
                "type": "string"
              }
            }
          }
        }
      },
      "ApplyForm": {
        "type": "object",
        "required": [
          "jobId",
          "jobSlug",
          "jobTitle",
          "firstName",
          "lastName",
          "email",
          "coverLetter",
          "cv",
          "candidateConsent"
        ],
        "properties": {
          "jobId": {
            "type": "string",
            "description": "The id from the feed (UUID)."
          },
          "jobSlug": {
            "type": "string",
            "description": "The slug from the feed."
          },
          "jobTitle": {
            "type": "string",
            "maxLength": 200
          },
          "firstName": {
            "type": "string",
            "maxLength": 100
          },
          "lastName": {
            "type": "string",
            "maxLength": 100
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 255
          },
          "phone": {
            "type": "string",
            "maxLength": 50
          },
          "location": {
            "type": "string",
            "maxLength": 200
          },
          "linkedinUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 500,
            "description": "Optional. Must be http or https."
          },
          "portfolioUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 500,
            "description": "Optional. Must be http or https."
          },
          "coverLetter": {
            "type": "string",
            "minLength": 20,
            "maxLength": 10000
          },
          "cv": {
            "type": "string",
            "format": "binary",
            "description": "A nonempty PDF, up to 10 MB."
          },
          "candidateConsent": {
            "type": "boolean",
            "const": true,
            "description": "The candidate has approved submitting their personal data to Pawanax for this role. REQUIRED, must be true.",
            "x-note": "REQUIRED=true. Enforced by the API: any other value, or a missing field, returns 400. Send it on every submission."
          },
          "experienceYears": {
            "type": "string",
            "description": "Academy roles only. Whole years, 0 to 60."
          },
          "domain": {
            "type": "string",
            "maxLength": 200,
            "description": "Academy roles only. Field of practice."
          },
          "aiPractice": {
            "type": "string",
            "description": "Academy roles only. How the candidate uses AI in practice."
          },
          "availability": {
            "type": "string",
            "enum": [
              "Within a month",
              "Within three months",
              "Part-time, around my current work",
              "Only for a named cohort",
              "Not sure yet"
            ],
            "description": "Academy roles only."
          },
          "teachingFormat": {
            "type": "string",
            "enum": [
              "In person, Nairobi",
              "Online, live sessions",
              "Recorded lessons with review",
              "Field sessions on a live queue",
              "A mix, to be agreed"
            ],
            "description": "Academy roles only."
          },
          "workStatus": {
            "type": "string",
            "enum": [
              "laid_off",
              "between_roles",
              "independent",
              "employed",
              "prefer_not_to_say"
            ],
            "description": "Academy roles only, optional. Private: used only for the application review and never shown publicly."
          },
          "cleanRoomAck": {
            "type": "string",
            "const": "true",
            "description": "Academy roles only. The candidate will not use a former employer's confidential material in any Academy work. Required for Academy roles."
          },
          "ref": {
            "type": "string",
            "pattern": "^[a-z0-9]{6,12}$",
            "description": "Optional referral code. Use the ref from the personal link a visitor shared (…/careers/faculty?ref=<shareCode>). Unknown or malformed values are dropped, never rejected."
          }
        }
      },
      "ApplySuccess": {
        "type": "object",
        "required": [
          "success",
          "application",
          "shareCode"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "application": {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Application reference. Keep it."
              }
            }
          },
          "message": {
            "type": "string"
          },
          "ok": {
            "type": "boolean",
            "const": true
          },
          "shareCode": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-hj-km-np-z2-9]{8}$",
            "description": "The applicant's own personal share code. Their link is https://pawanalife.com/careers/faculty?ref=<shareCode>. Null when the growth migration is not yet applied."
          }
        }
      },
      "AcademyWaitlist": {
        "type": "object",
        "required": [
          "type",
          "email"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "waitlist"
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254
          },
          "firstName": {
            "type": "string",
            "maxLength": 100
          },
          "persona": {
            "type": "string",
            "enum": [
              "student",
              "jobseeker",
              "switcher",
              "creator",
              "teacher",
              "founder",
              "curious",
              "organisation",
              "not_sure"
            ],
            "default": "not_sure"
          },
          "level": {
            "type": "string",
            "enum": [
              "literate",
              "fluent",
              "builder",
              "orchestrator",
              "steward",
              "not_sure"
            ],
            "default": "not_sure"
          },
          "website": {
            "type": "string",
            "maxLength": 0,
            "description": "Honeypot. Leave empty."
          },
          "ref": {
            "type": "string",
            "pattern": "^[a-z0-9]{6,12}$",
            "description": "Optional referral code. Use the ref from the personal link a visitor shared (…/careers/faculty?ref=<shareCode>). Unknown or malformed values are dropped, never rejected."
          }
        },
        "additionalProperties": false
      },
      "AcademyOrganisation": {
        "type": "object",
        "required": [
          "type",
          "fullName",
          "email",
          "organization",
          "role",
          "learners"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "organisation"
          },
          "fullName": {
            "type": "string",
            "maxLength": 120
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254
          },
          "organization": {
            "type": "string",
            "maxLength": 200
          },
          "role": {
            "type": "string",
            "maxLength": 120
          },
          "lane": {
            "type": "string",
            "enum": [
              "exec",
              "clinicians",
              "ops",
              "support",
              "finance",
              "hr",
              "engineering",
              "risk",
              "comms",
              "public",
              "multiple",
              "not_sure"
            ],
            "default": "not_sure"
          },
          "package": {
            "type": "string",
            "enum": [
              "workflow_map_pilot",
              "clinical_lanes",
              "support_whatsapp_lanes",
              "enterprise_lanes",
              "public_sector_lanes",
              "champions_retainer",
              "literate_cohort",
              "agent_build_sprint",
              "eval_ops_certificate",
              "forward_deployed_academy",
              "public_servant_track",
              "builder_residency",
              "not_sure"
            ],
            "default": "not_sure"
          },
          "learners": {
            "type": "string",
            "enum": [
              "Under 25",
              "25–100",
              "100–500",
              "500+"
            ]
          },
          "message": {
            "type": "string",
            "maxLength": 1000
          },
          "website": {
            "type": "string",
            "maxLength": 0,
            "description": "Honeypot. Leave empty."
          }
        },
        "additionalProperties": false
      },
      "AcademyInterestSuccess": {
        "type": "object",
        "required": [
          "success",
          "ok",
          "shareCode"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "ok": {
            "type": "boolean",
            "const": true
          },
          "shareCode": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-hj-km-np-z2-9]{8}$",
            "description": "Waitlist sign-ups only. The response has the same shape for a new email and for one already on the list: a duplicate gets the existing personal share code (or one is filled in). Organisation enquiries always return null."
          },
          "message": {
            "type": "string"
          }
        }
      }
    }
  }
}
