{"openapi":"3.1.0","info":{"title":"Linger API","version":"1.0.0","description":"Read your brand's offers, codes and followers, and report redemptions from any shop or till. Every call is scoped to the brand that owns the API key, and followers are only people who consented to that brand.","contact":{"email":"hello@uselinger.app"}},"servers":[{"url":"https://api.uselinger.app/v1"}],"security":[{"apiKey":[]}],"tags":[{"name":"Offers"},{"name":"Codes"},{"name":"Follows"},{"name":"Redemptions"}],"paths":{"/offers":{"get":{"summary":"List offers","description":"Your brand's offers at every show.\n\nScope: `offers:read`.","operationId":"listOffers","x-scope":"offers:read","parameters":[{"name":"show_id","in":"query","required":false,"description":"Only this show.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},{"name":"limit","in":"query","required":false,"description":"How many to return, 1 to 500.","schema":{"default":100,"type":"integer","minimum":1,"maximum":500}},{"name":"starting_after","in":"query","required":false,"description":"The `next_cursor` from the previous page.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}],"responses":{"200":{"description":"One page, oldest id first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferList"}}}},"400":{"description":"The query or body didn't validate; `detail` names the fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key doesn't have the scope this call needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 120 requests a minute with this key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Offers"]}},"/codes":{"get":{"summary":"List codes","description":"Offer codes from your pools, with whether each was given out or used.\n\nScope: `codes:read`.","operationId":"listCodes","x-scope":"codes:read","parameters":[{"name":"offer_id","in":"query","required":false,"description":"Only this offer.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},{"name":"status","in":"query","required":false,"description":"`unclaimed` (still in the pool), `claimed` (given to a follower) or `redeemed`.","schema":{"type":"string","enum":["unclaimed","claimed","redeemed"]}},{"name":"limit","in":"query","required":false,"description":"How many to return, 1 to 500.","schema":{"default":100,"type":"integer","minimum":1,"maximum":500}},{"name":"starting_after","in":"query","required":false,"description":"The `next_cursor` from the previous page.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}],"responses":{"200":{"description":"One page, oldest id first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeList"}}}},"400":{"description":"The query or body didn't validate; `detail` names the fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key doesn't have the scope this call needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 120 requests a minute with this key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Codes"]}},"/follows":{"get":{"summary":"List followers","description":"People who followed your brand and haven't withdrawn consent, with the code they were given. Anyone who unsubscribes drops out.\n\nScope: `follows:read`.","operationId":"listFollows","x-scope":"follows:read","parameters":[{"name":"show_id","in":"query","required":false,"description":"Only this show.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},{"name":"since","in":"query","required":false,"description":"Only follows from this time on (ISO 8601).","schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},{"name":"limit","in":"query","required":false,"description":"How many to return, 1 to 500.","schema":{"default":100,"type":"integer","minimum":1,"maximum":500}},{"name":"starting_after","in":"query","required":false,"description":"The `next_cursor` from the previous page.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}],"responses":{"200":{"description":"One page, oldest id first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowList"}}}},"400":{"description":"The query or body didn't validate; `detail` names the fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key doesn't have the scope this call needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 120 requests a minute with this key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Follows"]}},"/redemptions":{"post":{"summary":"Report a redemption","description":"Tell Linger a code was used, from your shop, till or agency. Safe to retry: the same `order_id` and code are recorded once.\n\nScope: `redemptions:write`.","operationId":"createRedemption","tags":["Redemptions"],"x-scope":"redemptions:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedemptionInput"}}}},"responses":{"200":{"description":"Already recorded; nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedemptionResult"}}}},"201":{"description":"Recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedemptionResult"}}}},"400":{"description":"The query or body didn't validate; `detail` names the fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key doesn't have the scope this call needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such code for your brand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The code exists but was never given to a follower.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 120 requests a minute with this key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"`Authorization: Bearer lk_live_…`. Owners create keys in the brand portal under Settings → API keys, and choose their scopes."}},"schemas":{"Show":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"starts_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"ends_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"}},"required":["id","name","starts_on","ends_on"],"description":"A food or drink show your brand has a stand at."},"Offer":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"show":{"$ref":"#/components/schemas/Show"},"title":{"type":"string","example":"£5 off your first order"},"terms":{"type":["string","null"]},"value_type":{"type":"string","enum":["fixed","percent","free_item","other"]},"value":{"description":"Pounds for `fixed`, percent for `percent`, otherwise null.","type":["number","null"]},"expires_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["id","show","title","terms","value_type","value","expires_at","created_at"],"description":"What a stand gives people who follow your brand. The newest one that hasn't expired is live."},"Code":{"type":"object","properties":{"code":{"type":"string","example":"LNG-7F3KQ9"},"offer_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["unclaimed","claimed","redeemed"]},"claimed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}],"description":"When it was given to a follower."},"redeemed_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]}},"required":["code","offer_id","status","claimed_at","redeemed_at"],"description":"A single-use offer code from the pool Linger generates for each offer."},"Follow":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string","format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"show":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"}},"required":["id","name"]},"stand_code":{"type":"string","example":"CKC"},"followed_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"consent":{"type":"object","properties":{"granted_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"wording_version":{"type":"string"}},"required":["granted_at","wording_version"]},"code":{"description":"The offer code this follower was given, if any.","type":["string","null"]}},"required":["id","email","show","stand_code","followed_at","consent","code"],"description":"Someone who followed your brand and hasn't withdrawn consent. People who unsubscribe drop out of this list."},"RedemptionInput":{"type":"object","properties":{"code":{"type":"string","minLength":3,"maxLength":64,"example":"LNG-7F3KQ9"},"channel":{"default":"online","type":"string","enum":["online","in_store"]},"order_id":{"description":"Your order reference. Repeating a call with the same order_id and code is safe.","type":"string","minLength":1,"maxLength":128},"order_value":{"example":18.5,"type":"number","minimum":0,"maximum":1000000},"currency":{"example":"GBP","type":"string","pattern":"^[A-Z]{3}$"},"redeemed_at":{"description":"Defaults to now.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["code"]},"RedemptionResult":{"type":"object","properties":{"status":{"type":"string","enum":["recorded","duplicate"]},"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["status","id"]},"Error":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_api_key","missing_scope","rate_limited","invalid_body","invalid_query","unknown_code","unclaimed_code"]},"detail":{}},"required":["error"]},"OfferList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Offer"}},"has_more":{"type":"boolean"},"next_cursor":{"description":"Pass as `starting_after` to get the next page.","type":["string","null"]}},"required":["data","has_more","next_cursor"]},"CodeList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Code"}},"has_more":{"type":"boolean"},"next_cursor":{"description":"Pass as `starting_after` to get the next page.","type":["string","null"]}},"required":["data","has_more","next_cursor"]},"FollowList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Follow"}},"has_more":{"type":"boolean"},"next_cursor":{"description":"Pass as `starting_after` to get the next page.","type":["string","null"]}},"required":["data","has_more","next_cursor"]}}},"x-scopes":{"offers:read":"Read offers","codes:read":"Read offer codes","follows:read":"Read followers (only people who consented to your brand)","redemptions:write":"Report redemptions"}}