{"openapi":"3.1.0","info":{"title":"Merchant Cleanup API","version":"1.0.0","description":"Cleans raw transaction descriptors into a merchant name, category and confidence score, plus a Business Days & Holidays add-on. Results are automated suggestions.","contact":{"name":"Andrew Jones","email":"violetvault@mail.grokbot.com"}},"servers":[{"url":"https://merchantcleanup.pages.dev"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Authorization: Bearer mcl_... (signed key). Plans set per-minute rate limits and batch caps, not monthly quotas."},"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"Item":{"type":"object","required":["descriptor"],"properties":{"descriptor":{"type":"string","maxLength":300},"mcc":{"type":"string","pattern":"^\\d{3,4}$"},"amount":{"type":"number"}}},"Result":{"type":"object","properties":{"input":{"type":"string"},"merchant":{"type":["string","null"]},"category":{"type":"string","enum":["income","transfer","groceries","meals","travel","auto_and_transport","shopping","software_and_subscriptions","office_and_supplies","utilities_and_phone","insurance","rent_and_housing","professional_services","contractors","advertising","fees_and_interest","taxes","payroll","health","entertainment","charity","education","other"]},"confidence":{"type":"number","minimum":0,"maximum":1},"match":{"type":"string"},"processor":{"type":["string","null"]},"mcc":{"type":"string"},"mcc_description":{"type":["string","null"]},"error":{"type":"string"}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}},"paths":{"/v1/clean":{"post":{"summary":"Clean one descriptor or a batch","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Item"},{"type":"object","required":["descriptors"],"properties":{"descriptors":{"type":"array","minItems":1,"maxItems":100,"description":"Per-plan cap: Free 10, Starter 100, Scale 100","items":{"oneOf":[{"type":"string","maxLength":300},{"$ref":"#/components/schemas/Item"}]}}}}]}}}},"responses":{"200":{"description":"Result (single) or {results, count} (batch)","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Result"},{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/Result"}},"count":{"type":"integer"}}}]}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Paid period ended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Batch over plan cap or body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (per key, per minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/demo":{"get":{"summary":"Keyless demo, one descriptor, rate-limited per IP","security":[],"parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","maxLength":300}}],"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Missing q","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Demo limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/holidays":{"get":{"summary":"National public holidays for a country and year (add-on)","parameters":[{"name":"country","in":"query","required":true,"schema":{"type":"string","enum":["US","CA","GB","IE","AU","NZ","DE","FR","ES","IT","NL","BE","AT","CH","SE","NO","DK","FI","PL","PT","CZ","JP","SG","MX","BR","ZA"]}},{"name":"subdiv","in":"query","required":false,"schema":{"type":"string"},"description":"Optional state/province/region code (see /v1/holidays/countries). Omitted: nationwide holidays only, except GB which defaults to ENG (England and Wales). Without subdiv, only nationwide public holidays are returned. Not covered anywhere: municipal or city holidays, bank-only or school holidays, half days, and one-off holidays declared after the data was built. Per country: US: Federal holidays only by default; state holidays (e.g. Cesar Chavez Day, Patriots Day) need subdiv. Federal observance does not mean banks or businesses close. CA: Federal statutory holidays only by default; provincial holidays (Family Day, Civic Holiday, Saint-Jean-Baptiste, etc.) need subdiv. GB: Defaults to England and Wales (ENG). Scotland (SCT) and Northern Ireland (NIR) bank holidays differ; pass subdiv. AU: National holidays only by default; state holidays such as the King's Birthday (dates vary by state), Labour Day and Melbourne Cup need subdiv. NZ: Regional anniversary days need subdiv. DE: Lander holidays (e.g. Epiphany, Corpus Christi, Reformation Day) need subdiv. FR: Alsace-Moselle and overseas holidays need subdiv. ES: Regional holidays need subdiv; local holidays are not covered. IT: Provincial patron saint days need subdiv. BE: Community holidays need subdiv. AT: State patron days need subdiv. CH: Very few nationwide holidays; most are cantonal, so pass subdiv. PT: Regional and municipal holidays not fully covered. JP: No subdivisions available. MX: Only official federal rest days; no subdivisions available. BR: State holidays need subdiv; municipal holidays are not covered."},{"name":"year","in":"query","required":true,"schema":{"type":"integer","minimum":2025,"maximum":2028}}],"responses":{"200":{"description":"{country, year, holidays:[{date,name}], count}","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad country, subdiv, year, date or range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Paid period ended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (per key, per minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/is-business-day":{"get":{"summary":"Is a date a business day (add-on)","parameters":[{"name":"country","in":"query","required":true,"schema":{"type":"string","enum":["US","CA","GB","IE","AU","NZ","DE","FR","ES","IT","NL","BE","AT","CH","SE","NO","DK","FI","PL","PT","CZ","JP","SG","MX","BR","ZA"]}},{"name":"subdiv","in":"query","required":false,"schema":{"type":"string"},"description":"Optional state/province/region code (see /v1/holidays/countries). Omitted: nationwide holidays only, except GB which defaults to ENG (England and Wales). Without subdiv, only nationwide public holidays are returned. Not covered anywhere: municipal or city holidays, bank-only or school holidays, half days, and one-off holidays declared after the data was built. Per country: US: Federal holidays only by default; state holidays (e.g. Cesar Chavez Day, Patriots Day) need subdiv. Federal observance does not mean banks or businesses close. CA: Federal statutory holidays only by default; provincial holidays (Family Day, Civic Holiday, Saint-Jean-Baptiste, etc.) need subdiv. GB: Defaults to England and Wales (ENG). Scotland (SCT) and Northern Ireland (NIR) bank holidays differ; pass subdiv. AU: National holidays only by default; state holidays such as the King's Birthday (dates vary by state), Labour Day and Melbourne Cup need subdiv. NZ: Regional anniversary days need subdiv. DE: Lander holidays (e.g. Epiphany, Corpus Christi, Reformation Day) need subdiv. FR: Alsace-Moselle and overseas holidays need subdiv. ES: Regional holidays need subdiv; local holidays are not covered. IT: Provincial patron saint days need subdiv. BE: Community holidays need subdiv. AT: State patron days need subdiv. CH: Very few nationwide holidays; most are cantonal, so pass subdiv. PT: Regional and municipal holidays not fully covered. JP: No subdivisions available. MX: Only official federal rest days; no subdivisions available. BR: State holidays need subdiv; municipal holidays are not covered."},{"name":"date","in":"query","required":true,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"{country, date, business_day, weekend, holiday}","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad country, subdiv, year, date or range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Paid period ended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (per key, per minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/business-days":{"get":{"summary":"Count business days in an inclusive range, max 731 days (add-on)","parameters":[{"name":"country","in":"query","required":true,"schema":{"type":"string","enum":["US","CA","GB","IE","AU","NZ","DE","FR","ES","IT","NL","BE","AT","CH","SE","NO","DK","FI","PL","PT","CZ","JP","SG","MX","BR","ZA"]}},{"name":"subdiv","in":"query","required":false,"schema":{"type":"string"},"description":"Optional state/province/region code (see /v1/holidays/countries). Omitted: nationwide holidays only, except GB which defaults to ENG (England and Wales). Without subdiv, only nationwide public holidays are returned. Not covered anywhere: municipal or city holidays, bank-only or school holidays, half days, and one-off holidays declared after the data was built. Per country: US: Federal holidays only by default; state holidays (e.g. Cesar Chavez Day, Patriots Day) need subdiv. Federal observance does not mean banks or businesses close. CA: Federal statutory holidays only by default; provincial holidays (Family Day, Civic Holiday, Saint-Jean-Baptiste, etc.) need subdiv. GB: Defaults to England and Wales (ENG). Scotland (SCT) and Northern Ireland (NIR) bank holidays differ; pass subdiv. AU: National holidays only by default; state holidays such as the King's Birthday (dates vary by state), Labour Day and Melbourne Cup need subdiv. NZ: Regional anniversary days need subdiv. DE: Lander holidays (e.g. Epiphany, Corpus Christi, Reformation Day) need subdiv. FR: Alsace-Moselle and overseas holidays need subdiv. ES: Regional holidays need subdiv; local holidays are not covered. IT: Provincial patron saint days need subdiv. BE: Community holidays need subdiv. AT: State patron days need subdiv. CH: Very few nationwide holidays; most are cantonal, so pass subdiv. PT: Regional and municipal holidays not fully covered. JP: No subdivisions available. MX: Only official federal rest days; no subdivisions available. BR: State holidays need subdiv; municipal holidays are not covered."},{"name":"start","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"end","in":"query","required":true,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"{business_days, weekend_days, holidays_on_weekdays, calendar_days}","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad country, subdiv, year, date or range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Paid period ended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (per key, per minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/add-business-days":{"get":{"summary":"Add or subtract business days (add-on)","parameters":[{"name":"country","in":"query","required":true,"schema":{"type":"string","enum":["US","CA","GB","IE","AU","NZ","DE","FR","ES","IT","NL","BE","AT","CH","SE","NO","DK","FI","PL","PT","CZ","JP","SG","MX","BR","ZA"]}},{"name":"subdiv","in":"query","required":false,"schema":{"type":"string"},"description":"Optional state/province/region code (see /v1/holidays/countries). Omitted: nationwide holidays only, except GB which defaults to ENG (England and Wales). Without subdiv, only nationwide public holidays are returned. Not covered anywhere: municipal or city holidays, bank-only or school holidays, half days, and one-off holidays declared after the data was built. Per country: US: Federal holidays only by default; state holidays (e.g. Cesar Chavez Day, Patriots Day) need subdiv. Federal observance does not mean banks or businesses close. CA: Federal statutory holidays only by default; provincial holidays (Family Day, Civic Holiday, Saint-Jean-Baptiste, etc.) need subdiv. GB: Defaults to England and Wales (ENG). Scotland (SCT) and Northern Ireland (NIR) bank holidays differ; pass subdiv. AU: National holidays only by default; state holidays such as the King's Birthday (dates vary by state), Labour Day and Melbourne Cup need subdiv. NZ: Regional anniversary days need subdiv. DE: Lander holidays (e.g. Epiphany, Corpus Christi, Reformation Day) need subdiv. FR: Alsace-Moselle and overseas holidays need subdiv. ES: Regional holidays need subdiv; local holidays are not covered. IT: Provincial patron saint days need subdiv. BE: Community holidays need subdiv. AT: State patron days need subdiv. CH: Very few nationwide holidays; most are cantonal, so pass subdiv. PT: Regional and municipal holidays not fully covered. JP: No subdivisions available. MX: Only official federal rest days; no subdivisions available. BR: State holidays need subdiv; municipal holidays are not covered."},{"name":"date","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"days","in":"query","required":true,"schema":{"type":"integer","minimum":-1000,"maximum":1000}}],"responses":{"200":{"description":"{date, days, result}","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad country, subdiv, year, date or range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Paid period ended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (per key, per minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/holidays/countries":{"get":{"summary":"Supported countries, subdivision codes, defaults and coverage gaps","security":[],"responses":{"200":{"description":"Countries"}}}},"/v1/categories":{"get":{"summary":"List categories","security":[],"responses":{"200":{"description":"Categories"}}}},"/v1/refresh":{"post":{"summary":"Exchange a paid key for a new one with a fresh expiry (up to 35 days) if the subscription is active","responses":{"200":{"description":"{key, plan, expires_at}"},"401":{"description":"Invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Subscription inactive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}