{
  "openapi": "3.1.0",
  "info": {
    "title": "HYLABS Partner API",
    "version": "1.0.0",
    "summary": "Test milestones for clients who chose to share them with their coach or coaching app.",
    "description": "Milestones only: ordered, in review, results back, report sent, call booked or held, next test due, refunded. Never a result, a report, the client's record, or contact details. Access is per client consent, which the client can withdraw in their HYLABS portal; on withdrawal you receive sharing.revoked and must delete what you hold within 7 days.\n\nAuthentication: `Authorization: Bearer hk_live_...` (or `hk_test_...` for the fixture sandbox, which answers every route with made-up clients and delivers real webhooks).\n\nWebhooks: each event is POSTed as JSON with headers `HYLABS-Event`, `HYLABS-Delivery` (the event id) and `HYLABS-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 over \"{t}.{raw body}\" with your webhook secret>`. Verify with a constant-time compare and reject `t` older than 5 minutes. Any 2xx within 10 seconds is success; failures are retried every 15 minutes for 24 hours.\n\nService area: collection centres in NSW, VIC, QLD and ACT.",
    "contact": {
      "name": "HYLABS",
      "url": "https://hylabs.com.au/developers"
    }
  },
  "servers": [
    {
      "url": "https://api.hylabs.com.au/v1"
    }
  ],
  "security": [
    {
      "bearerKey": []
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "summary": "Who you are",
        "operationId": "getMe",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Partner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Partner"
                }
              }
            }
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/clients": {
      "get": {
        "summary": "Shared clients and their milestones",
        "operationId": "listClients",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "parameters": [
          {
            "name": "updated_since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only clients changed after this time."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque, from next_cursor."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of clients",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "clients": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Client"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "generated_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "ISO 8601 with offset (Australia/Sydney)."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/clients/{client_ref}": {
      "get": {
        "summary": "One shared client",
        "operationId": "getClient",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "parameters": [
          {
            "name": "client_ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Client",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Client"
                }
              }
            }
          },
          "404": {
            "description": "Not shared with you",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/clients/by-external/{external_id}": {
      "get": {
        "summary": "One shared client by your own id",
        "operationId": "getClientByExternal",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "parameters": [
          {
            "name": "external_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Client",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Client"
                }
              }
            }
          },
          "404": {
            "description": "Not shared with you",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/events": {
      "get": {
        "summary": "What changed (for polling instead of webhooks)",
        "operationId": "listEvents",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Events after this time. Default: the last 7 days."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Events, oldest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Event"
                      }
                    },
                    "next_since": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "ISO 8601 with offset (Australia/Sydney)."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook": {
      "get": {
        "summary": "Your webhook",
        "operationId": "getWebhook",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook or null",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/Webhook"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Register or replace your webhook",
        "operationId": "putWebhook",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "https only"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created; the secret is in this response only",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "url is not https",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Remove your webhook",
        "operationId": "deleteWebhook",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook/test": {
      "post": {
        "summary": "Send a ping event to your webhook now",
        "operationId": "testWebhook",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivered": {
                      "type": "boolean"
                    },
                    "status_code": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "error": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "event_id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No webhook registered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook/deliveries": {
      "get": {
        "summary": "Recent delivery attempts, newest first",
        "operationId": "listDeliveries",
        "security": [
          {
            "bearerKey": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deliveries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Delivery"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Key missing, unknown or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner not active",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Throttled: 10 requests a second, burst 20",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "summary": "This document",
        "operationId": "getOpenApi",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 document"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "hk_live_... or hk_test_..."
      }
    },
    "schemas": {
      "Order": {
        "type": "object",
        "required": [
          "order_ref",
          "panel",
          "tier",
          "status",
          "ordered_at",
          "order_url"
        ],
        "properties": {
          "order_ref": {
            "type": "string",
            "example": "HY-TEST0109",
            "description": "The HYLABS order id."
          },
          "panel": {
            "type": "string",
            "example": "Physique Standard"
          },
          "tier": {
            "type": "string",
            "enum": [
              "results",
              "report"
            ],
            "description": "report when a doctor's written report and call are included (bought or upgraded)."
          },
          "status": {
            "type": "string",
            "enum": [
              "ordered",
              "form_expired",
              "in_review",
              "results_back",
              "report_sent",
              "call_booked",
              "call_held",
              "retest_due",
              "refunded"
            ],
            "description": "The single most advanced milestone. Precedence: refunded, retest_due, call_held, call_booked, report_sent, results_back, in_review, form_expired, ordered."
          },
          "ordered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "form_expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "in_review_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "results_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "report_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "call_booked_for": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "next_due_on": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Suggested next test, 90 days after the results or report went out."
          },
          "order_url": {
            "type": "string",
            "format": "uri",
            "description": "The partner's checkout link for the client's next test, carrying the external id when known."
          }
        }
      },
      "Client": {
        "type": "object",
        "required": [
          "client_ref",
          "sharing",
          "latest",
          "orders"
        ],
        "properties": {
          "client_ref": {
            "type": "string",
            "pattern": "^cl_[0-9a-f]{24}$",
            "description": "Stable per client per partner. Not reversible to a person."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your own id for the client, when the order link carried ext=."
          },
          "sharing": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "on"
                ]
              },
              "since": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "ISO 8601 with offset (Australia/Sydney)."
              }
            }
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "latest": {
            "$ref": "#/components/schemas/Order"
          },
          "orders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Order"
            },
            "description": "Newest first. Only orders attributed to you and shared by the client."
          }
        }
      },
      "Partner": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "coach",
              "vendor"
            ]
          },
          "status": {
            "type": "string"
          },
          "link": {
            "type": "string",
            "format": "uri"
          },
          "order_link": {
            "type": "string",
            "format": "uri"
          },
          "service_area": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "NSW",
              "VIC",
              "QLD",
              "ACT"
            ]
          },
          "children": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Vendors only: the coach codes under this partner."
          },
          "webhook": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Webhook"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "disabled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "secret": {
            "type": "string",
            "description": "Returned once, on creation. Verify HYLABS-Signature with it."
          }
        }
      },
      "Event": {
        "type": "object",
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "evt_7k2m9x1q4p8r3t6w0y5z2a1b"
          },
          "type": {
            "type": "string",
            "enum": [
              "order.created",
              "order.form_expired",
              "order.in_review",
              "results.ready",
              "report.sent",
              "call.booked",
              "call.held",
              "retest.due",
              "order.refunded",
              "sharing.granted",
              "sharing.revoked",
              "ping"
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "data": {
            "type": "object",
            "description": "client_ref, external_id and, for order events, the Order fields.",
            "additionalProperties": true
          }
        }
      },
      "Delivery": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "attempt": {
            "type": "integer"
          },
          "status_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "ok": {
            "type": "boolean"
          },
          "at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with offset (Australia/Sydney)."
          },
          "error": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorised",
                  "partner_inactive",
                  "not_found",
                  "invalid_request",
                  "rate_limited",
                  "server_error"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}