openapi: 3.0.3 info: title: Launch27 Account Authentication API description: 'Unofficial-but-real, actively used REST API for Launch27, a booking and scheduling platform for cleaning service businesses. Documented in a public Bitbucket wiki (https://bitbucket.org/awoo23/api-2.0/wiki/Home) linked from the launch27.com site footer, rather than in Launch27''s first-party docs.launch27.com knowledge base. The API is multi-tenant: every client account has its own subdomain. This document models the current v2.1 surface (the deprecated v2.0 surface is not modeled). Not every endpoint mentioned in the wiki is represented here in full schema detail; refer to the wiki pages linked per operation for complete field-by-field documentation.' version: '2.1' contact: name: API Evangelist email: kin@apievangelist.com servers: - url: https://{tenant}.launch27.com/v1 description: Production (per-tenant subdomain) variables: tenant: default: acme description: Launch27 client account subdomain. - url: https://{tenant}.l27.co/v1 description: Testing/sandbox (per-tenant subdomain) variables: tenant: default: acme-sandbox description: Launch27 sandbox account subdomain. security: - bearerAuth: [] tags: - name: Authentication description: Login and JWT bearer token issuance. paths: /login: post: operationId: login tags: - Authentication summary: Authenticate and obtain a JWT bearer token security: [] description: Authenticates a customer or staff user. Returns 401 on failure, 403 if the account is locked (5 invalid attempts within 1 minute locks the account for 5 minutes) or a 2FA token is required, 422 on schema errors. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' responses: '200': description: Authenticated successfully. content: application/json: schema: $ref: '#/components/schemas/LoginResponse' '401': $ref: '#/components/responses/Unauthorized' '403': description: Account locked out, or OTP token required. '422': $ref: '#/components/responses/ValidationError' components: schemas: LoginRequest: type: object required: - email - password properties: email: type: string format: email password: type: string token: type: string description: 6-digit 2FA token, if enabled. LoginResponse: type: object properties: id: type: integer email: type: string type: type: string example: Tenant::Customer first_name: type: string last_name: type: string bearer: type: string description: JWT bearer token. responses: Unauthorized: description: Missing/invalid Authorization header, or invalid credentials. ValidationError: description: JSON schema or data validation error. content: application/json: schema: type: object additionalProperties: true securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: 'JWT returned as `bearer` in the Login response. Sent as `Authorization: Bearer `. A legacy `email:token` form (Authorization: email:token) existed but was retired March 1, 2023.'