{"openapi":"3.0.3","info":{"title":"Lead API","version":"1.0.0","description":"Send prospects (leads) straight into the right club.\n\n## Getting started\n1. Ask us for an **API key**. A key gives access to one, several or all locations.\n2. Send every request with the header `x-api-key: <your key>`.\n3. Call `GET /locations` to see which location names your key may use.\n4. Send leads with `POST /leads`.\n\n## Rules\n- `first_name` is required. It may contain the full name; it is then split automatically.\n- Send **at least one** of `phone` or `email`.\n- `phone` may be sent in any common notation and is converted to **E.164** (e.g. `+32470123456`):\n  - international: `+32 470 12 34 56`, `0032470123456`, `+32 (0)470 12 34 56`\n  - national (Belgium, the Netherlands or Germany): `0470 12 34 56`, `06-12345678`, `0170 1234567`\n  - The country of a national number is recognised automatically (mobile ranges first, then Belgium, the Netherlands, Germany). If you know the country, send it in `country` to be sure. Numbers from other countries must include the country code.\n- `location` decides which club receives the lead. Matching ignores case, accents, spaces and dashes, and the \"HUPHUP\" prefix is optional (`Leuven`, `leuven` and `HUPHUP Leuven` all work). It may be left out only if your key has exactly one location.\n- `source` is optional: the channel the lead came from. Allowed values (not case-sensitive): `Facebook` (also `fb`), `Instagram` (also `ig`), `Website`, `TikTok`, `Google`, `Outreach`, `Mail`, `Referentie`, `Telefoon`, `Walk-In` (also `walkin`), `Anders`. Any other value is rejected.\n- Without `source`, it is derived from `campaign`: `FB` gives Facebook and `IG` gives Instagram, when written as a separate token (not directly preceded or followed by a letter, not case-sensitive), e.g. `FB_Autumn2026` or `Autumn-IG`. If neither or both appear, the source stays empty.\n- `campaign` is **required** when `source` is Facebook or Instagram, or when `source` is left empty. For other sources it is optional but recommended.\n- `form` is optional but recommended: the form or landing page the lead came from.\n- Sending the same person again (same phone or e-mail) updates the existing lead instead of creating a duplicate.\n\n## Error handling\n| Status | Meaning | What to do |\n|---|---|---|\n| `200` | Lead accepted | Nothing |\n| `401` | API key missing, wrong or disabled | Check the key; contact us if it should work |\n| `422` | Invalid data | Fix the fields listed in `errors`; do **not** retry unchanged |\n| `502` | Temporary problem | Retry later, e.g. after 1, 5 and 15 minutes |"},"servers":[{"url":"https://leadapi.huphup.be"}],"security":[{"ApiKey":[]}],"tags":[{"name":"Leads"},{"name":"Locations"}],"paths":{"/leads":{"post":{"tags":["Leads"],"summary":"Create a lead","operationId":"createLead","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"},"examples":{"full":{"summary":"All fields","value":{"first_name":"Jan","last_name":"Peeters","phone":"0470 12 34 56","country":"BE","email":"jan.peeters@example.com","location":"Leuven","campaign":"FB_Autumn2026","form":"Trial week landing page","source":"Facebook"}},"minimal":{"summary":"Minimal (source derived from campaign)","value":{"first_name":"Jan Peeters","email":"jan.peeters@example.com","location":"Leuven","campaign":"IG_Autumn2026"}}}}}},"responses":{"200":{"description":"Lead accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"},"example":{"status":"success","message":"lead successfully launched","location":"HUPHUP Leuven","phone":"+32470123456","country":"BE"}}}},"401":{"description":"API key missing, wrong or disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"status":"error","message":"invalid or missing API key"}}}},"422":{"description":"Invalid data. `errors` lists every problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"status":"error","message":"validation failed","errors":["phone could not be recognised as a BE/NL/DE number; send it with country code, e.g. +32470123456","unknown location \"Brussel\"; allowed: HUPHUP Gent, HUPHUP Leuven"]}}}},"502":{"description":"Temporary problem, retry later","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"status":"error","message":"lead could not be created, please retry later"}}}}}}},"/locations":{"get":{"tags":["Locations"],"summary":"List the locations your key may use","operationId":"listLocations","responses":{"200":{"description":"Location names to use in the `location` field","content":{"application/json":{"schema":{"type":"object","properties":{"locations":{"type":"array","items":{"type":"string"}}}},"example":{"locations":["HUPHUP Gent","HUPHUP Leuven"]}}}},"401":{"description":"API key missing, wrong or disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"status":"error","message":"invalid or missing API key"}}}}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your personal API key. Keep it secret: do not put it in website front-end code."}},"schemas":{"Lead":{"type":"object","required":["first_name"],"description":"At least one of `phone` or `email` is required.","properties":{"first_name":{"type":"string","maxLength":100,"description":"First name, or the full name.","example":"Jan"},"last_name":{"type":"string","maxLength":100,"description":"Last name. Optional.","example":"Peeters"},"phone":{"type":"string","description":"Phone number, E.164 or a common national notation (BE, NL, DE). Converted to E.164. Required if `email` is empty.","example":"0470 12 34 56"},"country":{"type":"string","enum":["BE","NL","DE"],"description":"Optional. Country of a phone number written without country code. Leave empty to detect automatically.","example":"BE"},"email":{"type":"string","format":"email","description":"E-mail address. Required if `phone` is empty.","example":"jan.peeters@example.com"},"location":{"type":"string","description":"Club that receives the lead. See `GET /locations`. Optional only if your key has one location.","example":"Leuven"},"campaign":{"type":"string","description":"Campaign name. Required when `source` is Facebook or Instagram or empty. `FB` / `IG` in the name sets the source when `source` is empty.","example":"FB_Autumn2026"},"form":{"type":"string","description":"Form or landing page name. Optional, strongly recommended.","example":"Trial week landing page"},"source":{"type":"string","enum":["Facebook","Instagram","Website","TikTok","Google","Outreach","Mail","Referentie","Telefoon","Walk-In","Anders"],"description":"Optional. Channel the lead came from. Not case-sensitive; `fb`, `ig` and `walkin` are also accepted. Stored with the spelling shown here.","example":"Facebook"}}},"Success":{"type":"object","properties":{"status":{"type":"string","enum":["success"]},"message":{"type":"string","enum":["lead successfully launched"]},"location":{"type":"string","description":"Full name of the club that received the lead."},"phone":{"type":"string","description":"The phone number as stored, in E.164. Only when a phone was sent."},"country":{"type":"string","description":"Country recognised from the phone number (ISO code, e.g. BE). Only when a phone was sent."}}},"Error":{"type":"object","properties":{"status":{"type":"string","enum":["error"]},"message":{"type":"string"},"errors":{"type":"array","items":{"type":"string"},"description":"Only on 422: every problem found."}}}}}}