{"openapi":"3.1.0","info":{"title":"Meet Me at the Fair — Promoter Events API","version":"1.0.0","description":"Submit and update your own events from your own system. New events are reviewed before they appear. Changes to a listed event are staged for review and never written directly. Listing is free."},"servers":[{"url":"https://meetmeatthefair.com"}],"security":[{"MmatfKey":[],"MmatfTimestamp":[],"MmatfRequestId":[],"MmatfSignature":[]}],"paths":{"/api/v1/promoter/events":{"get":{"summary":"List your events by external_id","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"One page, ordered by external_id. Pass next_cursor to get the next page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefPage"}}}},"401":{"description":"Missing auth headers, unknown or revoked key, stale timestamp, or bad signature.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Replayed request id, an event another organizer lists, or a create still in progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (60 requests per minute per key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}}}},"post":{"summary":"Create or update one event by your external_id","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Test without saving: the request is signed and checked as usual and nothing is written. Also accepted as \"dry_run\": true in the body. Answers 200 with a DryRunResult."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventUpsert"}}}},"responses":{"200":{"description":"The outcome, or (dry_run) the report of what would happen.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/WriteResult"},{"$ref":"#/components/schemas/DryRunResult"}]}}}},"201":{"description":"The outcome.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteResult"}}}},"401":{"description":"Missing auth headers, unknown or revoked key, stale timestamp, or bad signature.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Replayed request id, an event another organizer lists, or a create still in progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed; error.errors lists each field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (60 requests per minute per key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}}}}},"/api/v1/promoter/events/{external_id}":{"patch":{"summary":"Stage a partial update","parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:~-]{1,100}$"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Test without saving: the request is signed and checked as usual and nothing is written. Also accepted as \"dry_run\": true in the body. Answers 200 with a DryRunResult."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventFields"}}}},"responses":{"200":{"description":"The outcome, or (dry_run) the report of what would happen.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/WriteResult"},{"$ref":"#/components/schemas/DryRunResult"}]}}}},"401":{"description":"Missing auth headers, unknown or revoked key, stale timestamp, or bad signature.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown external_id for this key's promoter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Replayed request id, an event another organizer lists, or a create still in progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed; error.errors lists each field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (60 requests per minute per key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}}}}},"/api/v1/promoter/events/{external_id}/cancel":{"post":{"summary":"Ask for a cancellation (staged for review)","parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:~-]{1,100}$"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Test without saving: the request is signed and checked as usual and nothing is written. Also accepted as \"dry_run\": true in the body. Answers 200 with a DryRunResult."}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"reason":{"type":"string","maxLength":500},"dry_run":{"type":"boolean","description":"Test without saving (same as ?dry_run=true). Nothing is written."}}}}}},"responses":{"200":{"description":"The outcome, or (dry_run) the report of what would happen.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/WriteResult"},{"$ref":"#/components/schemas/DryRunResult"}]}}}},"401":{"description":"Missing auth headers, unknown or revoked key, stale timestamp, or bad signature.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown external_id for this key's promoter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Replayed request id, an event another organizer lists, or a create still in progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (60 requests per minute per key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}}}}}},"components":{"securitySchemes":{"MmatfKey":{"type":"apiKey","in":"header","name":"X-MMATF-Key"},"MmatfTimestamp":{"type":"apiKey","in":"header","name":"X-MMATF-Timestamp","description":"Unix seconds; must be within 300 s of our clock."},"MmatfRequestId":{"type":"apiKey","in":"header","name":"X-MMATF-Request-Id","description":"Unique per request, 1-100 printable characters."},"MmatfSignature":{"type":"apiKey","in":"header","name":"X-MMATF-Signature","description":"sha256=<hex HMAC-SHA256 of \"<timestamp>.<request_id>.<raw body>\" keyed with your secret>."}},"schemas":{"EventFields":{"type":"object","additionalProperties":false,"properties":{"name":{"type":["string","number","null"],"description":"Event name (required on create). Example: \"Fall Craft Fair\"."},"start_date":{"type":["string","number","null"],"description":"Start date (required on create). Example: \"2027-10-02\"."},"end_date":{"type":["string","number","null"],"description":"End date. Example: \"2027-10-03\"."},"daily_hours":{"type":["string","number","null"],"description":"Daily hours. Example: \"9am-4pm\"."},"venue_name":{"type":["string","number","null"],"description":"Venue name. Example: \"Town Hall\"."},"address":{"type":["string","number","null"],"description":"Address. Example: \"12 Main St\"."},"city":{"type":["string","number","null"],"description":"City (required on create). Example: \"Concord\"."},"state":{"type":["string","number","null"],"description":"State (required on create). Example: \"NH\"."},"zip":{"type":["string","number","null"],"description":"ZIP. Example: \"03301\"."},"description":{"type":["string","number","null"],"description":"Description. Example: \"60 juried artisans, food and music.\"."},"categories":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"maxItems":20}],"description":"Categories. A list, or one string separated by ; , or |. Example: \"Craft Fair; Holiday Market\". Unknown categories are left off with a warning."},"admission":{"type":["string","number","null"],"description":"Admission. Example: \"Free\"."},"vendor_application_url":{"type":["string","number","null"],"description":"Vendor application URL. Example: \"https://example.org/apply\"."},"application_deadline":{"type":["string","number","null"],"description":"Application deadline. Example: \"2027-08-15\"."},"booth_fee_min":{"type":["string","number","null"],"description":"Booth fee min. Example: \"75\"."},"booth_fee_max":{"type":["string","number","null"],"description":"Booth fee max. Example: \"125\"."},"event_url":{"type":["string","number","null"],"description":"Event URL. Example: \"https://example.org/fall-fair\"."},"image_url":{"type":["string","number","null"],"description":"Image URL. Example: \"https://example.org/poster.jpg\"."},"dry_run":{"type":"boolean","description":"Test without saving (same as ?dry_run=true). Nothing is written."}}},"EventUpsert":{"type":"object","additionalProperties":false,"required":["external_id","name","start_date","city","state"],"properties":{"external_id":{"type":"string","pattern":"^[A-Za-z0-9._:~-]{1,100}$"},"name":{"type":["string","number","null"],"description":"Event name (required on create). Example: \"Fall Craft Fair\"."},"start_date":{"type":["string","number","null"],"description":"Start date (required on create). Example: \"2027-10-02\"."},"end_date":{"type":["string","number","null"],"description":"End date. Example: \"2027-10-03\"."},"daily_hours":{"type":["string","number","null"],"description":"Daily hours. Example: \"9am-4pm\"."},"venue_name":{"type":["string","number","null"],"description":"Venue name. Example: \"Town Hall\"."},"address":{"type":["string","number","null"],"description":"Address. Example: \"12 Main St\"."},"city":{"type":["string","number","null"],"description":"City (required on create). Example: \"Concord\"."},"state":{"type":["string","number","null"],"description":"State (required on create). Example: \"NH\"."},"zip":{"type":["string","number","null"],"description":"ZIP. Example: \"03301\"."},"description":{"type":["string","number","null"],"description":"Description. Example: \"60 juried artisans, food and music.\"."},"categories":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"maxItems":20}],"description":"Categories. A list, or one string separated by ; , or |. Example: \"Craft Fair; Holiday Market\". Unknown categories are left off with a warning."},"admission":{"type":["string","number","null"],"description":"Admission. Example: \"Free\"."},"vendor_application_url":{"type":["string","number","null"],"description":"Vendor application URL. Example: \"https://example.org/apply\"."},"application_deadline":{"type":["string","number","null"],"description":"Application deadline. Example: \"2027-08-15\"."},"booth_fee_min":{"type":["string","number","null"],"description":"Booth fee min. Example: \"75\"."},"booth_fee_max":{"type":["string","number","null"],"description":"Booth fee max. Example: \"125\"."},"event_url":{"type":["string","number","null"],"description":"Event URL. Example: \"https://example.org/fall-fair\"."},"image_url":{"type":["string","number","null"],"description":"Image URL. Example: \"https://example.org/poster.jpg\"."},"dry_run":{"type":"boolean","description":"Test without saving (same as ?dry_run=true). Nothing is written."}}},"DryRunResult":{"type":"object","description":"A dry run: what the real request would return, and the report. Nothing was written. The request id was used.","required":["dry_run","external_id","would_return","report"],"properties":{"dry_run":{"type":"boolean","const":true},"external_id":{"type":"string"},"would_return":{"type":"object","description":"The real request's status, with its outcome (2xx) or its error (4xx). event_id is null for a would-be create.","properties":{"status":{"type":"integer"},"outcome":{"type":"string","enum":["created","update_staged","unchanged","cancel_staged"]},"event_id":{"type":["string","null"]},"error":{"$ref":"#/components/schemas/Error/properties/error"}}},"report":{"$ref":"#/components/schemas/TestReport"}}},"TestReport":{"type":"object","required":["title","read","outcome","problems"],"properties":{"title":{"type":"string"},"external_id":{"type":["string","null"]},"read":{"type":"array","description":"Each field as we read it, with where it came from.","items":{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string"},"value":{"type":["string","null"]},"source":{"type":["string","null"]}}}},"outcome":{"type":"object","required":["kind","summary"],"properties":{"kind":{"type":"string","enum":["new","match","skip"]},"summary":{"type":"string"},"reason":{"type":"string","description":"For skip: why."},"listing":{"type":"object","description":"For match: your listing. url is its public page, null until it is public.","properties":{"url":{"type":["string","null"]},"name":{"type":["string","null"]},"public":{"type":"boolean"}}},"changes":{"type":"array","description":"For match: the fields that would change.","items":{"type":"object","properties":{"field":{"type":"string"},"current":{"type":["string","null"]},"proposed":{"type":["string","null"]}}}}}},"problems":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"severity":{"type":"string","enum":["error","warning"]},"field":{"type":["string","null"]},"message":{"type":"string"}}}}}},"WriteResult":{"type":"object","required":["external_id","event_id","outcome","status"],"properties":{"external_id":{"type":"string"},"event_id":{"type":"string"},"outcome":{"type":"string","enum":["created","update_staged","unchanged","cancel_staged"]},"status":{"type":"string","description":"The event's current listing status."},"changes":{"type":"object","description":"For update_staged: field → {current, proposed}."},"not_compared":{"type":"array","items":{"type":"string"},"description":"Supplied fields the review does not compare on an existing event."},"warnings":{"type":"array","items":{"type":"string"}}}},"RefPage":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"external_id":{"type":"string"},"event_id":{"type":["string","null"]},"status":{"type":["string","null"]},"name":{"type":["string","null"]},"start_date":{"type":["string","null"]},"updated_at":{"type":["string","null"]}}}},"next_cursor":{"type":["string","null"]}}},"Error":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":["string","null"]},"message":{"type":"string"}}}}}}}}}}}