{"openapi":"3.0.3","info":{"title":"Fantastic Agents API","version":"1.0.0","description":"Recommendations of what real people chose and stand behind, ranked by fit to the request, each linked to a real listing, never a hallucinated one: catalog picks carry a purchase link to a matched merchant. Also audience insight and per-person profile lookup, grounded in real tracked behavior, not a guess."},"servers":[{"url":"https://fantastic.app"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A Fantastic Agents API key (starts with fi_). Make one on your settings page."}}},"paths":{"/agents/recommend":{"post":{"operationId":"recommend","summary":"Recommend things real people chose","description":"Ask for something to buy, watch, read, wear, eat, or book. Give it the request in natural language: the more detail, the more gets parsed out, a topic, a place, an exclusion. Results are never made up: catalog results are real listings with a purchase link from a matched merchant, and when the catalog has nothing, open-web results are marked source \"web\" with no offer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","description":"Where the conversation is now, in plain prose: what they are after, any place, dates or budget, and what they did with anything shown earlier (\"booked the first one\", \"passed on the Sony\"). Send it as the conversation moves. The request is read out of it, and reactions to results from earlier calls with the same session_id are saved to the user’s profile, so no separate curate call is needed for those."},"topic":{"type":"string","description":"What to recommend, in natural language. Not needed if state, image_base64, audio_base64, or url is given instead."},"session_id":{"type":"string","description":"A stable id for this conversation. Send the same one on every call so earlier results and reactions carry over."},"image_base64":{"type":"string","description":"A photo to recommend from or find matches for. Base64, JPEG/PNG/WebP, max 12MB."},"audio_base64":{"type":"string","description":"A spoken request. Base64, max 25MB."},"url":{"type":"string","description":"A link to recommend from or find alternatives to."},"fantastic_users":{"type":"array","items":{"type":"string"},"description":"Who it is for, by Fantastic username or email. One name matches their taste and skips what they have rejected, real interests looked up automatically, no separate lookup needed. Two or more blends their tastes, for gifts and group picks."},"age_range":{"type":"string","enum":["13-17","18-24","25-34","35-44","45-54","55-64","65+"],"description":"Favor results for an age range."},"gender":{"type":"string","enum":["male","female","non-binary"],"description":"Favor results for a gender."},"country":{"type":"string","description":"The user's country, as a two-letter code (US, GB, DE) or a name. Picks stores that sell there."},"limit":{"type":"integer","description":"Default 10, max 25."}}}}}},"responses":{"200":{"description":"Ranked recommendations, each linked to a real listing. Can be empty, with a note, when nothing fits the request well enough.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"429":{"description":"Too many requests. error.code is limit_reached when the monthly queries are used up, or rate_limited when too many came in the last minute (see retry_after and the Retry-After header).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"500":{"description":"Something went wrong on our end.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}}}}},"/agents/curate":{"post":{"operationId":"curate","summary":"Record what a person thinks of something","description":"Save something to a person’s profile, or record that they turned it down. Reactions to results from recommend are already saved when you pass state there; use this to save a link the user brings you, to save for someone else, to undo a save, or to import a list.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["fan","reject","remove"],"default":"fan","description":"What this person did. Defaults to fan."},"id":{"type":"string","description":"The id from a recommend result. Use this when you have it."},"url":{"type":"string","description":"The link, if you have no id."},"fantastic_user":{"type":"string","description":"Whose profile this is for, by Fantastic username or email. Without it the action is recorded against your own account, which is still real curation, just naming yourself instead of someone else."},"note":{"type":"string","description":"Why, in the person’s own words if you have them."},"items":{"type":"array","maxItems":50,"items":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]},"description":"Import several existing picks in one call, always fanned. When present, action, id and url are ignored. Capped at 50."}}}}}},"responses":{"200":{"description":"What was saved.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The action is not valid, or no id or url was given.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"403":{"description":"You aren't allowed to save recommendations for this person, or the account is suspended.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"404":{"description":"Person or item not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"429":{"description":"Too many requests. error.code is limit_reached when the monthly queries are used up, or rate_limited when too many came in the last minute (see retry_after and the Retry-After header).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"500":{"description":"Something went wrong on our end.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}}}}},"/agents/getAudience":{"post":{"operationId":"getAudience","summary":"Who cares about a topic and what they are like","description":"Demographics, interest strength, co-interests, trend, and top content, all in one call. Use this to understand a market; use recommend when someone wants something to buy.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["topic"],"properties":{"topic":{"type":"string","description":"The topic to analyze, e.g. \"streetwear\", \"yoga\", \"natural wine\"."},"compare_with":{"type":"string","description":"A second topic. Returns how much the two audiences overlap."},"age_range":{"type":"string","enum":["13-17","18-24","25-34","35-44","45-54","55-64","65+"],"description":"Only look at one age range."},"gender":{"type":"string","enum":["male","female","non-binary"],"description":"Only look at one gender."},"days":{"type":"integer","description":"How many days back to look for the trend. Default 90, max 365."},"limit":{"type":"integer","description":"Most rows to return for co_interests and top_content."}}}}}},"responses":{"200":{"description":"Who the audience is, with how sure we are about each part.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"429":{"description":"Too many requests. error.code is limit_reached when the monthly queries are used up, or rate_limited when too many came in the last minute (see retry_after and the Retry-After header).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"500":{"description":"Something went wrong on our end.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}}}}},"/agents/userInsights":{"post":{"operationId":"userInsights","summary":"Who a person is and what they care about","description":"By Fantastic username or email. Call it whenever a username or email is mentioned, or a Fantastic profile link is shared, its last URL segment is the username, so you know who they are. An email only answers whether that person is on Fantastic; it never returns their interests.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fantastic_users"],"properties":{"fantastic_users":{"type":"array","items":{"type":"string"},"description":"One or more people, each a Fantastic username or an email address, mixed freely. If given a profile link instead, extract the username from its last URL segment."},"topics":{"type":"array","items":{"type":"string"},"description":"Only show certain kinds of interests."}}}}}},"responses":{"200":{"description":"A profile and interests for each lookup, or found: false with a hint to invite them for an email we don't know.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"429":{"description":"Too many requests. error.code is limit_reached when the monthly queries are used up, or rate_limited when too many came in the last minute (see retry_after and the Retry-After header).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"500":{"description":"Something went wrong on our end.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}}}}},"/agents/send-invite":{"post":{"operationId":"sendInvite","summary":"Invite someone who is not on Fantastic yet","description":"Only call this after the user has confirmed they want to invite the person. Deduplicates automatically, will not re-send if the same email was invited in the last 30 days.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","description":"Email address to invite."}}}}}},"responses":{"200":{"description":"sent: true, or sent: false with a reason.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"429":{"description":"Too many requests. error.code is limit_reached when the monthly queries are used up, or rate_limited when too many came in the last minute (see retry_after and the Retry-After header).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"500":{"description":"Something went wrong on our end.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}}}}}}}