{"openapi":"3.1.0","info":{"title":"Bazzly Public API","version":"1.0.0","summary":"Reddit marketing for AI agents: find posts, draft replies and DMs, post through warmed-up accounts, track queues.","description":"Bazzly finds Reddit posts where a product fits, drafts replies and DMs, posts comments through warmed-up accounts so the user's own account carries no ban risk, and tracks everything in queues.\n\nAuthentication: every request carries `Authorization: Bearer <api key>` (or `X-Bazzly-Api-Key`). Keys are created at https://www.bazzly.ai/dashboard/account-settings?tab=api-keys and carry scopes `read`, `write` and `top-ups`; each operation lists the scope it needs.\n\nSpending: actions that cost credits return `{ requiresConfirmation: true, costs, message }` until repeated with `confirmCreditSpend: true`; actions that charge the card use `confirmPaymentSpend: true` the same way. Agents should show the preview to the user before confirming.\n\nLimits: 120 requests per minute per API key (HTTP 429 with a Retry-After hint in the message), 10 warmed-account orders per project per UTC day, 10 per 15 minutes per workspace (HTTP 429), and 10 product-URL analyses per user per hour. Cancelled subscriptions lose access when their paid period ends (HTTP 401).\n\nHuman-readable reference: https://www.bazzly.ai/docs/api. MCP server with the same capabilities: https://api.bazzly.ai/mcp (Streamable HTTP, same key).","contact":{"name":"Bazzly support","email":"support@bazzly.ai","url":"https://www.bazzly.ai/contact"},"termsOfService":"https://www.bazzly.ai/terms","license":{"name":"Proprietary","url":"https://www.bazzly.ai/terms"}},"externalDocs":{"description":"Bazzly REST API reference (markdown available at /docs/api.md)","url":"https://www.bazzly.ai/docs/api"},"servers":[{"url":"https://api.bazzly.ai/public/v1","description":"Production"}],"tags":[{"name":"Account","description":"The authenticated account and key."},{"name":"Projects","description":"One project per product: profile, targeting, credits."},{"name":"Credits","description":"Balances, top-ups and auto top-up."},{"name":"Opportunities","description":"Reddit posts scored against a project."},{"name":"Leads","description":"Reddit users who fit the product."},{"name":"Reply queue","description":"Comments requested, queued, posted, removed."},{"name":"DM queue","description":"DMs queued for the browser extension."},{"name":"Custom tasks","description":"Comment on or upvote any Reddit URL."},{"name":"Help","description":"Help center search and feedback for agents."}],"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"paths":{"/me":{"get":{"operationId":"getAccount","tags":["Account"],"summary":"Get the account and key in use","description":"Confirms the key works and shows which scopes it carries. Call this first.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects":{"get":{"operationId":"listProjects","tags":["Projects"],"summary":"List projects","description":"Every project on the account. Most accounts have one.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectList"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createProject","tags":["Projects"],"summary":"Create a project from a product URL","description":"Bazzly analyzes the page and generates the profile (name, description, ICP, keywords, subreddits, searches). The first project is created active; later ones are created as draft and need `activateProject`. At most two drafts can exist. Limited to 10 analyses per user per hour.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","pattern":"^(https?:\\/\\/)?([a-zA-Z0-9.-]+)(\\.[a-zA-Z]{2,})(\\/[^\\s]*)?$"}},"required":["url"],"additionalProperties":false}}}},"responses":{"201":{"description":"The new project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"An order limit was hit (10 warmed-account orders per project per UTC day, 10 per 15 minutes per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{projectId}":{"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id.","schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"getProject","tags":["Projects"],"summary":"Get a project","description":"One project with its full profile.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateProject","tags":["Projects"],"summary":"Update the project profile","description":"Partial update. Unknown fields are rejected. Autopilot settings are not exposed here; use the auto top-up endpoints for credits and the dashboard for pace and threshold.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"product_name":{"type":"string","minLength":1},"product_url":{"type":"string","pattern":"^(https?:\\/\\/)?([a-zA-Z0-9.-]+)(\\.[a-zA-Z]{2,})(\\/[^\\s]*)?$"},"product_description":{"type":"string","minLength":70,"maxLength":500},"icp":{"type":"string","minLength":20,"maxLength":400},"keywords":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50},"maxItems":10},"competitors":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50},"maxItems":6},"pain_points":{"type":"array","items":{"type":"string","minLength":1,"maxLength":120},"maxItems":5},"key_benefits":{"type":"array","items":{"type":"string","minLength":1,"maxLength":120},"maxItems":5},"key_features":{"type":"array","items":{"type":"string","minLength":1,"maxLength":120},"maxItems":6},"watched_subreddits":{"type":"array","items":{"allOf":[{"type":"string","minLength":1},{"type":"string","minLength":1,"maxLength":30}]},"maxItems":10},"dm_template":{"type":"string","maxLength":500},"discover_score_threshold":{"type":"integer","minimum":75,"maximum":100},"discover_classifications":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"scoring_custom_instructions":{"anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"reply_custom_instructions":{"anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{projectId}/activate":{"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id.","schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"activateProject","tags":["Projects"],"summary":"Activate a draft or inactive project","description":"Additional active projects are billed as extra seats at the plan price, prorated. Without `confirmPaymentSpend: true` this returns a preview of the charge. Show the preview to the user and get their approval before confirming.","security":[{"bearerAuth":["top-ups"]},{"apiKeyHeader":["top-ups"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirmPaymentSpend":{"type":"boolean"},"profile":{"type":"object","properties":{"product_name":{"type":"string","minLength":1},"product_url":{"type":"string","pattern":"^(https?:\\/\\/)?([a-zA-Z0-9.-]+)(\\.[a-zA-Z]{2,})(\\/[^\\s]*)?$"},"product_description":{"type":"string","minLength":70,"maxLength":500},"icp":{"type":"string","minLength":20,"maxLength":400},"keywords":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50},"maxItems":10},"competitors":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50},"maxItems":6},"pain_points":{"type":"array","items":{"type":"string","minLength":1,"maxLength":120},"maxItems":5},"key_benefits":{"type":"array","items":{"type":"string","minLength":1,"maxLength":120},"maxItems":5},"key_features":{"type":"array","items":{"type":"string","minLength":1,"maxLength":120},"maxItems":6},"watched_subreddits":{"type":"array","items":{"allOf":[{"type":"string","minLength":1},{"type":"string","minLength":1,"maxLength":30}]},"maxItems":10},"dm_template":{"type":"string","maxLength":500},"discover_score_threshold":{"type":"integer","minimum":75,"maximum":100},"discover_classifications":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"scoring_custom_instructions":{"anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"reply_custom_instructions":{"anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]}},"additionalProperties":false}},"additionalProperties":false}}}},"responses":{"200":{"description":"A payment preview when `confirmPaymentSpend` is absent or false; the action result when it is true.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/PaymentConfirmationPreview"},{"$ref":"#/components/schemas/Project"}]}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `top-ups` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{projectId}/credits":{"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id.","schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"getCredits","tags":["Credits"],"summary":"Get the credit balance","description":"Monthly plus topped-up credits. A warmed-account comment costs 10 credits, an upvote 0.2, an AI draft 0.1.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditBalance"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{projectId}/credits/manual":{"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id.","schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"topUpCredits","tags":["Credits"],"summary":"Buy credits","description":"Charges the card on file at 1 credit = 1 USD (5 to 1000). Without `confirmPaymentSpend: true` this returns a preview. Get the user's approval before confirming.","security":[{"bearerAuth":["top-ups"]},{"apiKeyHeader":["top-ups"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"number","minimum":5,"maximum":1000},"confirmPaymentSpend":{"type":"boolean"}},"required":["amount"],"additionalProperties":false}}}},"responses":{"200":{"description":"A payment preview when `confirmPaymentSpend` is absent or false; the action result when it is true.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/PaymentConfirmationPreview"},{"$ref":"#/components/schemas/CreditBalance"}]}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `top-ups` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{projectId}/credits/auto":{"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id.","schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"getAutoTopUp","tags":["Credits"],"summary":"Get auto top-up settings","description":"Auto top-up only fires while Autopilot is running. Includes the optional monthly budget cap and what auto top-ups have spent this UTC month.","security":[{"bearerAuth":["top-ups"]},{"apiKeyHeader":["top-ups"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoTopUp"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `top-ups` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"updateAutoTopUp","tags":["Credits"],"summary":"Update auto top-up settings","description":"Threshold 1 to 100, amount 25 to 250. Both are required when enabling. `auto_topup_monthly_cap` (25 to 10000 USD, or null for no cap; none by default) bounds auto top-ups per UTC calendar month; when reached, Autopilot pauses with reason `budget_cap_reached` and resumes on the 1st, when the cap is raised or removed, or after a manual top-up. `auto_topup_email_enabled` (off by default) sends a receipt for every auto top-up; the limit-reached email is always sent. Nothing changes the flag except this field. If a new `auto_topup_amount` is larger than the current cap, the cap is raised to four times the new amount; a cap below the amount is raised to the amount. Either adjustment is returned as `monthly_cap_raised_to` (null otherwise).","security":[{"bearerAuth":["top-ups"]},{"apiKeyHeader":["top-ups"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"auto_topup_enabled":{"type":"boolean"},"auto_topup_threshold":{"type":"integer","minimum":1,"maximum":100},"auto_topup_amount":{"type":"integer","minimum":25,"maximum":250},"auto_topup_monthly_cap":{"anyOf":[{"type":"integer","minimum":25,"maximum":10000},{"type":"null"}]},"auto_topup_email_enabled":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoTopUpUpdateResult"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `top-ups` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/opportunities":{"get":{"operationId":"listOpportunities","tags":["Opportunities"],"summary":"List Reddit posts worth replying to","description":"Scored posts across the account or one project. `recent` posts come from subreddit monitoring; `top-ranked` posts rank on Google for a project search and keep sending traffic for months. Sorted by fit score by default.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"parameters":[{"name":"projectId","in":"query","required":false,"description":"Restrict to one project.","schema":{"type":"string","format":"uuid"}},{"name":"filter","in":"query","required":false,"description":"all (default), recent, or top-ranked.","schema":{"type":"string","enum":["all","recent","top-ranked"]}},{"name":"sort","in":"query","required":false,"description":"score (default) or time.","schema":{"type":"string","enum":["score","time"]}},{"name":"limit","in":"query","required":false,"description":"1 to 100, default 50.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Pagination offset.","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Ranked opportunities.","content":{"application/json":{"schema":{"type":"object","required":["data","total","nextOffset"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Opportunity"}},"total":{"type":"integer","description":"Total matching rows."},"nextOffset":{"type":["integer","null"],"description":"Offset for the next page, or null when exhausted."}}}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/opportunities/{id}":{"parameters":[{"name":"id","in":"path","required":true,"description":"Prefixed opportunity id: ro-{uuid} or sp-{uuid}.","schema":{"type":"string","pattern":"^(ro|sp)-[0-9a-f-]{8,}$"}}],"get":{"operationId":"getOpportunity","tags":["Opportunities"],"summary":"Get an opportunity","description":"One scored post with its match reason and any saved draft.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateOpportunity","tags":["Opportunities"],"summary":"Save a draft reply or skip the opportunity","description":"Provide `proposed_reply` (3 to 4000 chars, empty string clears) and/or `skip: true` to dismiss it.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"proposed_reply":{"anyOf":[{"type":"string","minLength":3,"maxLength":4000},{"type":"string","const":""}]},"skip":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionResult"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/opportunities/{id}/generate-reply":{"parameters":[{"name":"id","in":"path","required":true,"description":"Prefixed opportunity id: ro-{uuid} or sp-{uuid}.","schema":{"type":"string","pattern":"^(ro|sp)-[0-9a-f-]{8,}$"}}],"post":{"operationId":"generateOpportunityReply","tags":["Opportunities"],"summary":"Draft a reply with AI","description":"Writes a comment that answers the post and mentions the product where it fits, following the project's reply instructions. Costs 0.1 credits. Without `confirmCreditSpend: true` returns a cost preview.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirmCreditSpend":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"A cost preview when `confirmCreditSpend` is absent or false; the action result when it is true.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CreditConfirmationPreview"},{"$ref":"#/components/schemas/DraftResult"}]}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leads":{"get":{"operationId":"listLeads","tags":["Leads"],"summary":"List leads","description":"Reddit users who wrote posts that matched the project and have not been contacted.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"parameters":[{"name":"projectId","in":"query","required":false,"description":"Restrict to one project.","schema":{"type":"string","format":"uuid"}},{"name":"sort","in":"query","required":false,"description":"score (default) or time.","schema":{"type":"string","enum":["score","time"]}},{"name":"limit","in":"query","required":false,"description":"1 to 100, default 50.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Pagination offset.","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Ranked leads.","content":{"application/json":{"schema":{"type":"object","required":["data","total","nextOffset"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Lead"}},"total":{"type":"integer","description":"Total matching rows."},"nextOffset":{"type":["integer","null"],"description":"Offset for the next page, or null when exhausted."}}}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leads/{id}":{"parameters":[{"name":"id","in":"path","required":true,"description":"Lead id.","schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"getLead","tags":["Leads"],"summary":"Get a lead","description":"One lead with the posts that qualified them.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lead"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateLead","tags":["Leads"],"summary":"Save a draft DM or skip the lead","description":"Provide `proposed_dm` (3 to 2000 chars) and/or `skip: true`.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"proposed_dm":{"anyOf":[{"type":"string","minLength":3,"maxLength":2000},{"type":"string","const":""}]},"skip":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionResult"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leads/{id}/generate-dm":{"parameters":[{"name":"id","in":"path","required":true,"description":"Lead id.","schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"generateLeadDm","tags":["Leads"],"summary":"Draft a DM with AI","description":"Fills the project DM template from the lead's posts. Costs 0.1 credits. Without `confirmCreditSpend: true` returns a cost preview.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirmCreditSpend":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"A cost preview when `confirmCreditSpend` is absent or false; the action result when it is true.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CreditConfirmationPreview"},{"$ref":"#/components/schemas/DraftResult"}]}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/queue/reply":{"get":{"operationId":"listReplyQueue","tags":["Reply queue"],"summary":"List requested comments","description":"Own-account and warmed-account comments. Rows contain source (own or external), raw stored status, canCancel, reddit_url once posted, comment_credits_refunded, and upvoteOrders (ordered, delivered and refunded upvotes per order). Default filter: queued. Use status=all for every status.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"parameters":[{"name":"projectId","in":"query","required":false,"description":"Restrict to one project.","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"queued (default), posted, removed, cancelled or all.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"1 to 200, default 100.","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"offset","in":"query","required":false,"description":"Pagination offset.","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Queue items, newest first.","content":{"application/json":{"schema":{"type":"object","required":["data","total","nextOffset"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ReplyQueueItem"}},"total":{"type":"integer","description":"Total matching rows."},"nextOffset":{"type":["integer","null"],"description":"Offset for the next page, or null when exhausted."}}}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"queueReply","tags":["Reply queue"],"summary":"Post a comment","description":"`high_karma` posts through a warmed-up account (10 credits; content must be in English with no links, bare domains, subreddit links (r/name) or disguised links (like name dot com) and must not near-duplicate a warmed-account comment from the last 30 days (fleet-wide for 8+ words, same subreddit and last 7 days only for shorter text), strict subreddits may refuse); `extension` queues it for the user's own account (free). Optional upvotes: 1 to 10, 0.2 credits each, mode `custom` or `rank_first`. Without `confirmCreditSpend: true` returns a cost preview.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"opportunityId":{"type":"string","pattern":"^(ro|sp)-[0-9a-f-]{8,}$"},"deliveryMethod":{"type":"string","enum":["extension","high_karma"]},"content":{"type":"string","minLength":3,"maxLength":4000},"upvotes":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"maximum":10},"mode":{"type":"string","enum":["custom","rank_first"]}},"required":["quantity","mode"],"additionalProperties":false},"confirmCreditSpend":{"type":"boolean"}},"required":["opportunityId","deliveryMethod"],"additionalProperties":false}}}},"responses":{"200":{"description":"A cost preview when `confirmCreditSpend` is absent or false; the action result when it is true.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CreditConfirmationPreview"},{"$ref":"#/components/schemas/QueueResult"}]}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"An order limit was hit (10 warmed-account orders per project per UTC day, 10 per 15 minutes per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/queue/reply/{id}/cancel":{"parameters":[{"name":"id","in":"path","required":true,"description":"Reply queue item id.","schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"cancelReply","tags":["Reply queue"],"summary":"Cancel an eligible queued comment","description":"Own-account replies can be cancelled while queued. Eligible warmed-account replies can be cancelled only before posting preparation starts, including while waiting for a slot. Not every warmed-account reply supports cancellation: check canCancel on GET /queue/reply. Cancellation and comment/pending-upvote refunds are atomic. Returns { success: true, refunded: true } for a paid cancellation; repeated cancellation is safe. If dispatch wins the race or cancellation is unavailable, returns 400 without cancelling or refunding. This does not delete a published Reddit comment.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionResult"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/queue/reply/{id}/retry":{"parameters":[{"name":"id","in":"path","required":true,"description":"Reply queue item id.","schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"retryReply","tags":["Reply queue"],"summary":"Requeue a removed or cancelled comment","description":"Requeuing a warmed-account comment costs 10 credits again and may require edited text.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionResult"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/queue/dm":{"get":{"operationId":"listDmQueue","tags":["DM queue"],"summary":"List queued DMs","description":"DMs are sent from the user's own Reddit account by the browser extension; the API queues them.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"parameters":[{"name":"projectId","in":"query","required":false,"description":"Restrict to one project.","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"queued, sent or failed. Omit for all.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"1 to 200, default 100.","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"offset","in":"query","required":false,"description":"Pagination offset.","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Queue items, newest first.","content":{"application/json":{"schema":{"type":"object","required":["data","total","nextOffset"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DmQueueItem"}},"total":{"type":"integer","description":"Total matching rows."},"nextOffset":{"type":["integer","null"],"description":"Offset for the next page, or null when exhausted."}}}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"queueDm","tags":["DM queue"],"summary":"Queue a DM","description":"Uses the saved `proposed_dm`; generate or save one first. Free. Top-ranked (sp-) opportunities cannot be DMed.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"string","enum":["opportunity","lead"]},"sourceId":{"type":"string","minLength":1}},"required":["source","sourceId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueueResult"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/queue/dm/{id}/cancel":{"parameters":[{"name":"id","in":"path","required":true,"description":"DM queue item id.","schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"cancelDm","tags":["DM queue"],"summary":"Cancel a queued DM","description":"Removes the DM from the queue before the extension sends it.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionResult"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/queue/dm/{id}/retry":{"parameters":[{"name":"id","in":"path","required":true,"description":"DM queue item id.","schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"retryDm","tags":["DM queue"],"summary":"Requeue a failed DM","description":"Puts a failed DM back in the queue for the next extension run.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionResult"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/custom-tasks/thread":{"get":{"operationId":"getRedditThread","tags":["Custom tasks"],"summary":"Read a Reddit thread before drafting a custom task","description":"Returns what is needed to write or refine `content` for `POST /custom-tasks`: the post, the parent comment when the URL points at a comment, up to 10 existing comments the new reply would sit beside, whether warmed accounts can post on this URL (`postable`), and the subreddit's rules (`subredditRules`: promo strictness, whether a removed comment is refunded, and whether replies are unavailable there). Accepts share links (`/s/...`). Spends no credits and creates nothing. Limited to 30 lookups per 10 minutes per workspace.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"parameters":[{"name":"redditUrl","in":"query","required":true,"description":"Reddit post, comment or share URL (https, reddit.com hosts only).","schema":{"type":"string","minLength":1,"maxLength":2000}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedditThread"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Thread lookup limit reached (30 per 10 minutes per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/custom-tasks":{"post":{"operationId":"createCustomTask","tags":["Custom tasks"],"summary":"Comment on any Reddit URL through a warmed-up account","description":"Always posts a comment, on any post or comment URL; for upvotes without a comment use `POST /custom-tasks/upvotes`. The text is either supplied or drafted. Supplied: send `content` and it is posted as written. It must be in English; links, bare domains, subreddit links (r/name), disguised links (like name dot com) and near-duplicates of a warmed-account comment from the last 30 days (fleet-wide for 8+ words, same subreddit and last 7 days only for shorter text) are rejected; an order that was declined or cancelled before posting does not count. Read the thread first with `GET /custom-tasks/thread`. Drafted: omit `content` and Bazzly writes the comment from the project profile and queues it in the same call, so the text is not reviewed before it is committed. Drafting adds the AI draft fee, and the text comes back as `generatedReply`. Every call costs the warmed-account comment fee. Optional `upvotes` are delivered to the new comment once it is published. Without `confirmCreditSpend: true` returns a cost preview. A comment removed within 48 hours of posting is refunded, except in a subreddit that is strict on promotion: the preview and the result both carry `subredditRules`, and `removalRefund: false` means a removal there is not refunded. Requires an active paid subscription.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"projectId":{"type":"string","format":"uuid"},"redditUrl":{"type":"string","minLength":1},"content":{"type":"string","minLength":3,"maxLength":4000},"upvotes":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"maximum":10},"mode":{"type":"string","enum":["custom","rank_first"]}},"required":["quantity","mode"],"additionalProperties":false},"confirmCreditSpend":{"type":"boolean"}},"required":["projectId","redditUrl"],"additionalProperties":false}}}},"responses":{"200":{"description":"A cost preview when `confirmCreditSpend` is absent or false; the action result when it is true.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CreditConfirmationPreview"},{"$ref":"#/components/schemas/QueueResult"}]}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"An order limit was hit (10 warmed-account orders per project per UTC day, 10 per 15 minutes per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/custom-tasks/upvotes":{"post":{"operationId":"createUpvoteTask","tags":["Custom tasks"],"summary":"Order upvotes on any Reddit URL, without a comment","description":"Orders 1 to 10 upvotes on an existing post or comment. No comment is written or posted. Costs 0.2 credits per upvote and delivery starts right away. Only one active upvote order per URL per workspace. The order appears in `GET /queue/reply?status=posted` under the returned `externalTaskId`. Without `confirmCreditSpend: true` returns a cost preview. Requires an active paid subscription.","security":[{"bearerAuth":["write"]},{"apiKeyHeader":["write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"projectId":{"type":"string","format":"uuid"},"redditUrl":{"type":"string","minLength":1,"maxLength":2000},"quantity":{"type":"integer","minimum":1,"maximum":10},"confirmCreditSpend":{"type":"boolean"}},"required":["projectId","redditUrl","quantity"],"additionalProperties":false}}}},"responses":{"200":{"description":"A cost preview when `confirmCreditSpend` is absent or false; the action result when it is true.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CreditConfirmationPreview"},{"$ref":"#/components/schemas/UpvoteTaskResult"}]}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/help/search":{"get":{"operationId":"searchHelp","tags":["Help"],"summary":"Search the help center","description":"Use before answering any question about how Bazzly works. Returns ranked articles with the most relevant paragraph as a snippet.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"parameters":[{"name":"q","in":"query","required":true,"description":"Search terms, 2 to 200 chars.","schema":{"type":"string","minLength":2,"maxLength":200}},{"name":"limit","in":"query","required":false,"description":"1 to 10, default 5.","schema":{"type":"integer","minimum":1,"maximum":10}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HelpSearchResult"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/help/articles":{"get":{"operationId":"listHelpArticles","tags":["Help"],"summary":"List help articles","description":"Every published article without its body.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HelpArticleSummary"}}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/help/articles/{slug}":{"parameters":[{"name":"slug","in":"path","required":true,"description":"Article slug from search or list.","schema":{"type":"string"}}],"get":{"operationId":"getHelpArticle","tags":["Help"],"summary":"Read a help article","description":"The full article as markdown.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HelpArticle"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/feedback":{"post":{"operationId":"submitFeedback","tags":["Help"],"summary":"Send feedback to the Bazzly team","description":"Report a bug, a missing capability, a docs gap or an unanswered question on the user's behalf. Any key may call this. No human needs to write to support afterwards.","security":[{"bearerAuth":["read"]},{"apiKeyHeader":["read"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"string","enum":["bug","missing_feature","docs","question","other"]},"message":{"type":"string","minLength":3,"maxLength":4000},"projectId":{"type":"string","format":"uuid"},"context":{"type":"object","additionalProperties":{}}},"required":["category","message"],"additionalProperties":false}}}},"responses":{"201":{"description":"Feedback stored and forwarded to the team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackReceipt"}}}},"400":{"description":"Validation error. The message names the offending field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key created at https://www.bazzly.ai/dashboard/account-settings?tab=api-keys. Keys carry scopes: read, write, top-ups."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-Bazzly-Api-Key","description":"Alternative to the bearer header."}},"schemas":{"Error":{"type":"object","required":["statusCode","message"],"properties":{"statusCode":{"type":"integer"},"message":{"type":"string"},"error":{"type":"string"}}},"Account":{"type":"object","required":["id","apiKey"],"properties":{"id":{"type":"string","format":"uuid","description":"Workspace user id all data is scoped to."},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"apiKey":{"type":"object","required":["id","name","scopes"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","write","top-ups"]}}}}}},"Project":{"type":"object","description":"One product: its profile, watched subreddits, Google searches, filters, DM template and credits.","required":["id","product_name","status"],"properties":{"id":{"type":"string","format":"uuid"},"product_name":{"type":"string"},"product_url":{"type":["string","null"]},"product_description":{"type":["string","null"]},"status":{"type":"string","enum":["active","draft","inactive","locked"]},"icp":{"type":["string","null"]},"keywords":{"type":"array","items":{"type":"string"}},"competitors":{"type":"array","items":{"type":"string"}},"pain_points":{"type":"array","items":{"type":"string"}},"key_benefits":{"type":"array","items":{"type":"string"}},"key_features":{"type":"array","items":{"type":"string"}},"watched_subreddits":{"type":"array","items":{"type":"string"}},"dm_template":{"type":["string","null"]},"discover_score_threshold":{"type":["integer","null"],"minimum":75,"maximum":100},"discover_classifications":{"type":["array","null"],"items":{"type":"string"}},"scoring_custom_instructions":{"type":["string","null"]},"reply_custom_instructions":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}},"additionalProperties":true},"ProjectList":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}}},"RedditPost":{"type":"object","properties":{"title":{"type":"string"},"body":{"type":["string","null"]},"subreddit":{"type":"string"},"author":{"type":["string","null"]},"url":{"type":"string","format":"uri"},"score":{"type":["integer","null"]},"numComments":{"type":["integer","null"]},"createdUtc":{"type":["number","null"],"description":"Unix seconds."}},"additionalProperties":true},"Opportunity":{"type":"object","description":"A Reddit post scored against a project. `ro-` ids come from subreddit monitoring, `sp-` ids from posts ranking on Google.","required":["id","source","projectId","fitScore"],"properties":{"id":{"type":"string","pattern":"^(ro|sp)-[0-9a-f-]{8,}$"},"source":{"type":"string","enum":["recent","top-ranked"]},"projectId":{"type":"string","format":"uuid"},"post":{"$ref":"#/components/schemas/RedditPost"},"fitScore":{"type":"number","minimum":0,"maximum":1},"fitReason":{"type":["string","null"]},"classification":{"type":["string","null"]},"proposedReply":{"type":["string","null"]},"serpQuery":{"type":["string","null"],"description":"Top-ranked only: the Google query the thread ranks for."},"serpPosition":{"type":["integer","null"]},"estimatedTraffic":{"type":["number","null"],"description":"Estimated monthly visits to the thread."}},"additionalProperties":true},"Lead":{"type":"object","description":"A Reddit user who wrote a post that matched the project.","required":["id","projectId","redditUsername"],"properties":{"id":{"type":"string","format":"uuid"},"projectId":{"type":"string","format":"uuid"},"redditUsername":{"type":"string"},"bestFitScore":{"type":["number","null"],"minimum":0,"maximum":1},"proposedDm":{"type":["string","null"]},"reasons":{"type":"array","items":{"type":"object","properties":{"reasonType":{"type":"string"},"fitScore":{"type":["number","null"]},"fitReason":{"type":["string","null"]},"post":{"$ref":"#/components/schemas/RedditPost"}},"additionalProperties":true}}},"additionalProperties":true},"UpvoteOrder":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"quantity":{"type":"integer","description":"Upvotes ordered."},"status":{"type":"string","description":"pending_comment (waiting for the comment to go live), pending or submitted (in delivery), completed (all delivered), partial (some delivered, the rest refunded), failed or refunded (none delivered, fully refunded), cancelled."},"creditsCost":{"type":"number","description":"Credits charged when the order was placed."},"creditsRefunded":{"type":"number","description":"Credits returned for upvotes that were not delivered."},"upvotesDelivered":{"type":"integer","description":"Upvotes that registered on the public score. A vote Reddit discards is not counted and is refunded. Final once `completedAt` is set; Reddit's displayed score can still move afterwards."},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time","description":"When delivery ended and any refund was issued; null while in progress."}},"additionalProperties":true},"SubredditRules":{"type":"object","description":"What Bazzly knows about the subreddit before a warmed-account comment is ordered there. `GET /custom-tasks/thread` and a confirmed `POST /custom-tasks` read and classify a subreddit Bazzly has not seen before, so `unknown` there means it could not be read. The cost preview only reports what is already known: look the thread up first.","required":["promoStrictness","removalRefund","note","warmedAccountReplies"],"properties":{"promoStrictness":{"type":"string","enum":["strict","moderate","lenient","unknown"],"description":"How hard the subreddit moderates promotion."},"removalRefund":{"type":"boolean","description":"Whether a warmed-account comment removed within 48 hours of posting is refunded. False in a strict subreddit: the comment can still be ordered, at your own risk."},"note":{"type":["string","null"],"description":"User-facing explanation when `removalRefund` is false."},"warmedAccountReplies":{"type":"object","required":["available","reason","message"],"properties":{"available":{"type":"boolean","description":"False when an order in this subreddit would be declined and refunded instead of posted."},"reason":{"type":["string","null"],"enum":["approved_members_only","removes_new_comments","account_requirement",null],"description":"approved_members_only: only approved members can comment. removes_new_comments: the subreddit currently removes all new comments, so replies are paused. account_requirement: a karma or account age rule no warmed account meets yet."},"message":{"type":["string","null"],"description":"User-facing explanation."}}}}},"ReplyQueueItem":{"type":"object","required":["id","status","source","canCancel","upvoteOrders"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","queued","sent","failed","dismissed"]},"source":{"type":"string","enum":["own","external"]},"canCancel":{"type":"boolean","description":"Current cancellation eligibility. Rechecked atomically when cancelling; a true value is not a reservation."},"content":{"type":["string","null"]},"reddit_url":{"type":["string","null"],"format":"uri","description":"Permalink of the live comment; null until posted."},"sent_at":{"type":["string","null"],"format":"date-time"},"error_message":{"type":["string","null"]},"comment_credits_refunded":{"type":"boolean","description":"True once the comment fee was returned (never posted, cancelled, or removed with a refund)."},"upvoteOrders":{"type":"array","description":"Upvote orders placed on this comment, oldest first: ordered, delivered and refunded. Empty when none were ordered.","items":{"$ref":"#/components/schemas/UpvoteOrder"}},"reply_opportunities":{"type":["object","null"],"additionalProperties":true},"serp_reddit_posts":{"type":["object","null"],"additionalProperties":true},"custom_tasks":{"type":["object","null"],"additionalProperties":true}},"additionalProperties":true},"DmQueueItem":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","sent","failed"]},"redditUsername":{"type":["string","null"]},"content":{"type":["string","null"]},"sentAt":{"type":["string","null"],"format":"date-time"},"errorMessage":{"type":["string","null"]},"lead":{"$ref":"#/components/schemas/Lead"}},"additionalProperties":true},"CreditBalance":{"type":"object","required":["balance"],"properties":{"balance":{"type":"object","required":["monthly","topped_up","total"],"properties":{"monthly":{"type":"number","description":"Resets each billing period; unused credits expire."},"topped_up":{"type":"number","description":"Purchased credits; never expire, spent after monthly."},"total":{"type":"number"}}}}},"AutoTopUp":{"type":"object","properties":{"enabled":{"type":"boolean"},"threshold":{"type":["integer","null"],"description":"Top up when credits drop below this."},"amount":{"type":["integer","null"],"description":"Credits added per auto top-up (1 credit = 1 USD)."},"monthly_cap":{"type":["integer","null"],"description":"Most auto top-ups may charge per UTC calendar month, in USD. null means no cap. Manual top-ups never count."},"spent_this_month":{"type":"number","description":"USD charged by auto top-ups since the 1st of the current UTC month."},"topup_email_enabled":{"type":"boolean","description":"Whether every successful auto top-up sends a receipt email. Off by default."}}},"AutoTopUpUpdateResult":{"type":"object","properties":{"success":{"type":"boolean"},"monthly_cap_raised_to":{"type":["integer","null"],"description":"Set when the cap was adjusted for you: raised to four times a new auto_topup_amount that exceeded it, or raised to the amount when the cap you passed was below one top-up."}}},"CreditConfirmationPreview":{"type":"object","description":"Returned instead of acting when `confirmCreditSpend` is not true. Repeat the call with the flag to perform it.","required":["requiresConfirmation","costs","message"],"properties":{"requiresConfirmation":{"type":"boolean","const":true},"projectId":{"type":"string","format":"uuid"},"costs":{"type":"object","properties":{"highKarmaComment":{"type":"number"},"upvotes":{"type":"number"},"total":{"type":"number"}},"additionalProperties":true},"subredditRules":{"oneOf":[{"$ref":"#/components/schemas/SubredditRules"},{"type":"null"}],"description":"`POST /custom-tasks` only. Check `removalRefund` and `warmedAccountReplies.available` before confirming."},"message":{"type":"string"}},"additionalProperties":true},"PaymentConfirmationPreview":{"type":"object","description":"Returned instead of charging when `confirmPaymentSpend` is not true. Repeat the call with the flag to charge the card on file.","required":["requiresConfirmation","message"],"properties":{"requiresConfirmation":{"type":"boolean","const":true},"message":{"type":"string"}},"additionalProperties":true},"DraftResult":{"type":"object","required":["success","text","creditsSpent"],"properties":{"success":{"type":"boolean"},"text":{"type":"string"},"creditsSpent":{"type":"number"}}},"QueueResult":{"type":"object","required":["success","creditsSpent"],"properties":{"success":{"type":"boolean"},"externalTaskId":{"type":"string","format":"uuid","description":"Set for warmed-account comments."},"outreachQueueId":{"type":"string","format":"uuid","description":"Set for own-account comments and DMs."},"creditsSpent":{"type":"number"},"subredditRules":{"oneOf":[{"$ref":"#/components/schemas/SubredditRules"},{"type":"null"}],"description":"`POST /custom-tasks` only: the rules that applied to this order."}},"additionalProperties":true},"UpvoteTaskResult":{"type":"object","required":["success","customTaskId","externalTaskId","upvoteOrderId","creditsSpent"],"properties":{"success":{"type":"boolean"},"customTaskId":{"type":"string","format":"uuid"},"externalTaskId":{"type":"string","format":"uuid","description":"Id of the matching `GET /queue/reply` item."},"upvoteOrderId":{"type":"string","format":"uuid"},"creditsSpent":{"type":"number"}}},"RedditThread":{"type":"object","required":["redditUrl","targetType","subreddit","subredditRules","post","parentComment","comments","postable","unpostable"],"properties":{"redditUrl":{"type":"string","description":"Canonical URL (share links resolved, trackers stripped). Pass this to `POST /custom-tasks`."},"targetType":{"type":"string","enum":["post","comment"],"description":"What a custom task on this URL replies to."},"subreddit":{"type":["string","null"]},"subredditRules":{"oneOf":[{"$ref":"#/components/schemas/SubredditRules"},{"type":"null"}],"description":"Subreddit-level rules for a warmed-account comment here: whether a removal is refunded and whether an order would be declined. Null only when the subreddit is unknown. Independent of `postable`, which is about this URL."},"post":{"type":"object","required":["title","body","author","createdUtc"],"properties":{"title":{"type":["string","null"]},"body":{"type":["string","null"],"description":"Self text; null for link and image posts."},"author":{"type":["string","null"]},"createdUtc":{"type":["number","null"],"description":"Unix seconds."}}},"parentComment":{"type":["object","null"],"description":"The comment the reply would answer. Null when `targetType` is `post`.","required":["id","author","body","createdUtc"],"properties":{"id":{"type":"string"},"author":{"type":["string","null"]},"body":{"type":["string","null"]},"createdUtc":{"type":["number","null"],"description":"Unix seconds."}}},"comments":{"type":"array","maxItems":10,"description":"Existing comments next to where the reply would land, in Reddit's default order: top-level comments for a post URL, direct replies to the parent comment for a comment URL. Removed, deleted, stickied and AutoModerator comments are skipped. Bodies are cut at 1500 characters.","items":{"type":"object","required":["author","body","score","createdUtc"],"properties":{"author":{"type":"string"},"body":{"type":"string"},"score":{"type":["integer","null"]},"createdUtc":{"type":["number","null"],"description":"Unix seconds."}}}},"postable":{"type":"boolean","description":"False when `POST /custom-tasks` would reject this URL."},"unpostable":{"type":["object","null"],"required":["reason","message"],"properties":{"reason":{"type":"string","enum":["removed","deleted","archived","locked","nsfw","unavailable"]},"message":{"type":"string","description":"User-facing explanation."}}}}},"ActionResult":{"type":"object","required":["success"],"properties":{"refunded":{"type":"boolean","description":"True after a paid cancellation and its refunds commit."},"success":{"type":"boolean"}},"additionalProperties":true},"HelpSearchResult":{"type":"object","required":["query","results","helpCenterUrl"],"properties":{"query":{"type":"string"},"helpCenterUrl":{"type":"string","format":"uri"},"results":{"type":"array","items":{"type":"object","required":["slug","title","url","score","snippet"],"properties":{"slug":{"type":"string"},"title":{"type":"string"},"excerpt":{"type":"string"},"category":{"type":"string"},"url":{"type":"string","format":"uri"},"score":{"type":"number"},"snippet":{"type":"string","description":"The most relevant paragraph; often enough to answer from."}}}}}},"HelpArticleSummary":{"type":"object","required":["slug","title","url"],"properties":{"slug":{"type":"string"},"title":{"type":"string"},"excerpt":{"type":"string"},"category":{"type":"string"},"categoryTitle":{"type":"string"},"updatedAt":{"type":"string"},"url":{"type":"string","format":"uri"}}},"HelpArticle":{"allOf":[{"$ref":"#/components/schemas/HelpArticleSummary"},{"type":"object","required":["body"],"properties":{"body":{"type":"string","description":"Full article as markdown."}}}]},"FeedbackReceipt":{"type":"object","required":["id","receivedAt","message"],"properties":{"id":{"type":"string","format":"uuid"},"receivedAt":{"type":"string","format":"date-time"},"message":{"type":"string"}}}}}}