{"openapi":"3.1.0","info":{"title":"RewindRewind API","version":"1.0.0"},"servers":[{"url":"http://www.rewindrewind.com"}],"components":{"securitySchemes":{"bearerApiKey":{"type":"http","scheme":"bearer"},"sessionCookie":{"type":"apiKey","in":"cookie","name":"better-auth.session_token"}},"schemas":{"Error":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}},"Pagination":{"type":"object","required":["limit","next_cursor","has_more"],"properties":{"limit":{"type":"integer","minimum":1,"maximum":500},"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}}},"Project":{"type":"object","properties":{"id":{"type":"string"},"account_id":{"type":"string"},"account_name":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"role":{"type":"string","enum":["admin","member"]},"retention_days":{"type":"integer"},"uptime_enabled":{"type":"boolean"},"uptime_url":{"type":["string","null"],"format":"uri"},"disabled_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"AccountMember":{"type":"object","properties":{"id":{"type":"string","description":"Membership id — the handle for role changes and removal"},"user_id":{"type":"string"},"email":{"type":"string","format":"email"},"name":{"type":["string","null"]},"role":{"type":"string","enum":["admin","member"]},"created_at":{"type":"string","format":"date-time"}}},"AccountInvite":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","member"]},"status":{"type":"string","enum":["pending","accepted","expired"],"description":"accepted covers both an invitee who consumed the emailed link and an invited email that already belonged to a user, which is granted membership immediately"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"accepted_at":{"type":["string","null"],"format":"date-time"},"accepted_by_user_id":{"type":["string","null"]},"member_user_id":{"type":["string","null"],"description":"Set once the invited email holds a membership in the account"},"invited_by":{"type":["object","null"],"description":"Null when the invite was sent with an admin API key rather than by a signed-in user","properties":{"user_id":{"type":"string"},"email":{"type":["string","null"],"format":"email"},"name":{"type":["string","null"]}}}}},"HealthRuleSpecification":{"type":"object","required":["name","measure","window","operator","missing_data","severity","display_order","dashboard_visible","enabled"],"properties":{"name":{"type":"string","maxLength":120},"description":{"type":["string","null"]},"measure":{"type":"object","description":"Typed event, uptime, exception, or comparison measure"},"environment":{"type":["string","null"]},"source":{"type":["string","null"]},"unit":{"type":["string","null"],"description":"Free-text label appended after the value (ignored when format is currency)"},"scale":{"type":["number","null"],"description":"Divide the raw value by this before display, e.g. 100 for cents → dollars. Presentation only: the target is still compared in raw units"},"format":{"type":["string","null"],"enum":["number","currency","percent",null],"description":"Presentational format applied to the scaled value, target, and reason"},"currency":{"type":["string","null"],"description":"ISO 4217 code used when format is currency; defaults to USD"},"property_filters":{"type":"object","maxProperties":8},"window":{"type":"object","description":"Rolling, calendar, or instantaneous effective window"},"operator":{"type":"string","enum":[">",">=","<","<=","=","!="]},"target":{"type":"number"},"missing_data":{"type":"string","enum":["unknown","zero"]},"severity":{"type":"string","enum":["required","informational"]},"display_order":{"type":"integer","minimum":0},"dashboard_visible":{"type":"boolean"},"enabled":{"type":"boolean"}}},"MetricSpecification":{"type":"object","description":"A displayable measure with no expectation: no operator, target, or severity, and no contribution to health status.","required":["name","measure","window","display_order","dashboard_visible","enabled"],"properties":{"name":{"type":"string","maxLength":120},"description":{"type":["string","null"]},"measure":{"type":"object","description":"Typed event, uptime, exception, or comparison measure (identical vocabulary to a health rule)"},"window":{"type":"object","description":"Rolling, calendar, or instantaneous effective window"},"environment":{"type":["string","null"]},"source":{"type":["string","null"]},"unit":{"type":["string","null"],"description":"Free-text label appended after the value (ignored when format is currency)"},"scale":{"type":["number","null"],"description":"Divide the raw value by this before display, e.g. 100 for cents → dollars"},"format":{"type":["string","null"],"enum":["number","currency","percent",null],"description":"Presentational format applied to the scaled value"},"currency":{"type":["string","null"],"description":"ISO 4217 code used when format is currency; defaults to USD"},"property_filters":{"type":"object","maxProperties":8},"display_order":{"type":"integer","minimum":0},"dashboard_visible":{"type":"boolean"},"enabled":{"type":"boolean"}}},"Event":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"environment":{"type":"string"},"release":{"type":["string","null"]},"timestamp":{"type":"string","format":"date-time"},"received_at":{"type":"string","format":"date-time"}}},"Issue":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"title":{"type":"string"},"environment":{"type":"string"},"first_seen":{"type":"string","format":"date-time"},"last_seen":{"type":"string","format":"date-time"},"count":{"type":"integer"}}},"IssueComment":{"type":"object","properties":{"id":{"type":"string"},"issue_id":{"type":"string"},"body":{"type":"string"},"author_name":{"type":"string","description":"Display name: the signed-in user's name, or the API key's name for API/CLI comments."},"author_type":{"type":"string","enum":["user","api_key"]},"author_user_id":{"type":["string","null"]},"author_api_key_id":{"type":["string","null"]},"edited":{"type":"boolean","description":"True once the comment has been edited. The original text is retained server-side."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"edited_at":{"type":["string","null"],"format":"date-time"}}},"SupportConversation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"project_id":{"type":"string"},"status":{"type":"string","enum":["open","resolved","spam"]},"subject":{"type":"string"},"contact_name":{"type":["string","null"]},"contact_email":{"type":["string","null"],"format":"email"},"contact_phone":{"type":["string","null"]},"identity_id":{"type":["string","null"]},"anonymous_id":{"type":["string","null"]},"traits":{"type":"object","additionalProperties":true},"context":{"type":"object","additionalProperties":true},"custom_fields":{"type":"object","additionalProperties":{"type":"string"},"description":"Values for this project's custom_fields definitions, keyed by field key."},"assignee_user_id":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"SupportMessage":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"direction":{"type":"string","enum":["inbound","outbound"]},"author_kind":{"type":"string","enum":["requester","team_member","system","agent"]},"body":{"type":"string"},"channel":{"type":"string","enum":["email","sms","web","in_app","phone","other"]},"occurred_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"delivery_status":{"type":["string","null"],"enum":["recorded","queued","sent","bounced","failed",null]}}}}},"security":[{"sessionCookie":[]},{"bearerApiKey":[]}],"paths":{"/api/health":{"get":{"summary":"Health check","security":[],"responses":{"200":{"description":"Service is healthy"}}}},"/v1/events":{"post":{"summary":"Ingest one event","security":[{"bearerApiKey":[]}],"responses":{"202":{"description":"Event accepted"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/v1/support":{"post":{"summary":"Create a support conversation","description":"Public-key intake. Support requests have independent rate limits, remain available through telemetry spend pauses, and consume no credits.","security":[{"bearerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["subject","message"],"properties":{"subject":{"type":"string","maxLength":200},"message":{"type":"string","maxLength":20000},"contact":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"}}},"identity_id":{"type":"string"},"anonymous_id":{"type":"string"},"traits":{"type":"object","additionalProperties":true},"context":{"type":"object","additionalProperties":true},"custom_fields":{"type":"object","additionalProperties":{"type":"string"},"description":"Values keyed by the project's custom_fields definitions (see GET .../support/settings). Values for undefined keys are dropped."}}}}}},"responses":{"201":{"description":"Conversation created"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/v1/events/batch":{"post":{"summary":"Ingest a batch of events","security":[{"bearerApiKey":[]}],"responses":{"202":{"description":"Events accepted"}}}},"/v1/exceptions":{"post":{"summary":"Ingest an exception","description":"A 202 with `dropped: true` and a `noise_rule` object means the exception matched a noise rule: it was discarded before storage, created no issue, sent no notification, and was not metered. Note `dropped` is distinct from the `suppressed` flag on a stored exception, which only means repeat notifications are silenced for that issue.","security":[{"bearerApiKey":[]}],"responses":{"202":{"description":"Exception accepted, or dropped by a noise rule"}}}},"/v1/sentry/envelope":{"post":{"summary":"Ingest a Sentry event envelope","description":"Accepts Sentry envelope payloads through the RewindRewind tunnel. Authenticate with the public project key as a bearer token or sentry_key query parameter.","security":[{"bearerApiKey":[]}],"parameters":[{"name":"sentry_key","in":"query","schema":{"type":"string"},"description":"Public project key; an alternative to Authorization."}],"requestBody":{"required":true,"content":{"application/x-sentry-envelope":{"schema":{"type":"string"}}}},"responses":{"202":{"description":"Envelope accepted"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/v1/source-maps":{"post":{"summary":"Upload a source map","security":[{"bearerApiKey":[]}],"responses":{"202":{"description":"Source map accepted"}}}},"/v1/visit":{"post":{"summary":"Record a pre-aggregated daily visit / DAU signal","description":"Increments a per-project-per-UTC-day counter (never stored as a durable event, never metered). Send once per page load. Known bots are skipped.","security":[{"bearerApiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["environment"],"properties":{"environment":{"type":"string","maxLength":64},"visitor_id":{"type":"string","description":"Stable per-visitor id for unique-visitor dedup; falls back to a hash of IP + user-agent."}}}}}},"responses":{"204":{"description":"Visit recorded (or skipped as a bot)"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/v1/projects/{project_id}/visits":{"get":{"summary":"Read the daily visit / DAU series","description":"Gap-filled per-UTC-day series of { day, total_hits, unique_visitors }. Defaults to the last 30 days.","security":[{"bearerApiKey":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","schema":{"type":"string","format":"date","description":"Inclusive start day (YYYY-MM-DD, UTC)."}},{"name":"to","in":"query","schema":{"type":"string","format":"date","description":"Inclusive end day (YYYY-MM-DD, UTC)."}},{"name":"environment","in":"query","schema":{"type":"string","maxLength":64}}],"responses":{"200":{"description":"Daily visit series"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/usage":{"get":{"summary":"Read current account usage and billing projection","description":"Available to account administrators through a browser session or an admin API key.","parameters":[{"name":"account","in":"query","schema":{"type":"string"},"description":"Account id. Optional for an account-scoped admin API key."}],"responses":{"200":{"description":"Current usage, allowance, projected credits and cost, spend cap, and billing state"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/organization/members":{"get":{"summary":"List account members and every invite with its status","parameters":[{"name":"account_id","in":"query","schema":{"type":"string"},"description":"Only needed for cookie auth on a user who belongs to more than one account; an admin key is already scoped to one."}],"responses":{"200":{"description":"Members and invites","content":{"application/json":{"schema":{"type":"object","properties":{"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"members":{"type":"array","items":{"$ref":"#/components/schemas/AccountMember"}},"invites":{"type":"array","items":{"$ref":"#/components/schemas/AccountInvite"}}}}}}},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}},"post":{"summary":"Invite a member by email","description":"Emails a sign-in link. An email that already belongs to a user is granted the membership immediately; a new email gets it when the link is used. Re-inviting an address updates the existing invite in place.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","member"],"default":"member"},"account_id":{"type":"string"}}}}}},"responses":{"201":{"description":"Invite sent"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/organization/members/{member_id}":{"patch":{"summary":"Change a member's role","parameters":[{"name":"member_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"properties":{"role":{"type":"string","enum":["admin","member"]},"account_id":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated member"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Remove a member from the account","parameters":[{"name":"member_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"account_id","in":"query","schema":{"type":"string"},"description":"Only needed for cookie auth on a user who belongs to more than one account; an admin key is already scoped to one."}],"responses":{"200":{"description":"Removed member"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/organization/invites":{"get":{"summary":"List invites, optionally filtered by status","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["all","pending","accepted","expired"],"default":"all"}},{"name":"account_id","in":"query","schema":{"type":"string"},"description":"Only needed for cookie auth on a user who belongs to more than one account; an admin key is already scoped to one."}],"responses":{"200":{"description":"Invites","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"invites":{"type":"array","items":{"$ref":"#/components/schemas/AccountInvite"}}}}}}},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/organization/invites/{invitation_id}":{"get":{"summary":"Read one invite and its current status","parameters":[{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"account_id","in":"query","schema":{"type":"string"},"description":"Only needed for cookie auth on a user who belongs to more than one account; an admin key is already scoped to one."}],"responses":{"200":{"description":"Invite"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Revoke a pending invite","parameters":[{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"account_id","in":"query","schema":{"type":"string"},"description":"Only needed for cookie auth on a user who belongs to more than one account; an admin key is already scoped to one."}],"responses":{"200":{"description":"Revoked invite"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/organization/invites/{invitation_id}/resend":{"post":{"summary":"Re-send an invite email and reset its 24-hour expiry","parameters":[{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Invite resent"},"4XX":{"description":"Request error","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/projects":{"get":{"summary":"List projects available to the signed-in user","responses":{"200":{"description":"Project list"}}},"post":{"summary":"Create a project","responses":{"201":{"description":"Project created"}}}},"/api/projects/{project_id}":{"get":{"summary":"Read a project","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Project"}}},"patch":{"summary":"Update project settings","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"uptime_enabled":{"type":"boolean"},"uptime_url":{"type":["string","null"],"format":"uri"}}}}}},"responses":{"200":{"description":"Updated project"}}},"delete":{"summary":"Disable a project","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Disabled project"}}}},"/api/projects/{project_id}/notifications":{"get":{"summary":"Read the project's notification configuration","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Notification configuration"}}},"patch":{"summary":"Update the project notification default and repeat cadence","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated notification configuration"}}}},"/api/projects/{project_id}/support":{"get":{"summary":"List support conversations","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["active","resolved","spam","all"]}}],"responses":{"200":{"description":"Support conversations"}}}},"/api/projects/{project_id}/support/settings":{"get":{"summary":"Read hosted support form settings","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Support form settings"}}},"patch":{"summary":"Update hosted support form settings","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated support form settings"}}}},"/api/projects/{project_id}/support/erase":{"post":{"summary":"Erase support data for a requester","description":"Deletes matching conversations and cascades through messages, notes, note revisions, lifecycle, and delivery records.","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"identity_id":{"type":"string"},"anonymous_id":{"type":"string"},"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"Erasure complete"}}}},"/api/projects/{project_id}/support/{conversation_id}":{"get":{"summary":"Read a support conversation and its complete thread","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Conversation detail"}}},"patch":{"summary":"Change support status or assignment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated conversation"}}}},"/api/projects/{project_id}/support/{conversation_id}/messages":{"post":{"summary":"Record an outbound support response","description":"Records a response sent through another channel. V1 does not deliver it.","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body","channel"],"properties":{"body":{"type":"string"},"channel":{"type":"string","enum":["email","sms","web","in_app","phone","other"]},"occurred_at":{"type":["string","number"],"description":"ISO date-time or epoch milliseconds"}}}}}},"responses":{"201":{"description":"Response recorded with delivery status recorded"}}}},"/api/projects/{project_id}/support/{conversation_id}/notes":{"post":{"summary":"Create an internal support note","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Internal note created"}}}},"/api/projects/{project_id}/support/{conversation_id}/notes/{note_id}":{"patch":{"summary":"Edit an internal note and append a revision","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"note_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Internal note updated"}}}},"/api/projects/{project_id}/noise/catalog":{"get":{"summary":"List the built-in known-noise catalog as this project sees it","description":"Every curated entry, its vendor, its written reason, its match specification, and whether this project has it switched on. Entries are on by default.","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalog entries"}}}},"/api/projects/{project_id}/noise/catalog/{entry_id}":{"post":{"summary":"Turn one catalog entry on or off for this project","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"entry_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Updated entry state"},"404":{"description":"Unknown catalog entry","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/projects/{project_id}/noise/rules":{"get":{"summary":"List this project's noise rules","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Noise rules, including disabled ones"}}},"post":{"summary":"Create a noise rule","description":"Matches exactly on platform, exception type and the normalized message, optionally narrowed by environment or a frame URL origin. Route, line, column, function name and release are never matchable. A written reason is required, and a message with no discriminating identifier is rejected unless frame_url_origin pins provenance. By default, existing matching issues are closed as ignored with the rule named as the reason; their counts, occurrences and payloads are preserved.","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["platform","exception_type","message","reason"],"properties":{"platform":{"type":"string","maxLength":120,"description":"Exact, e.g. javascript"},"exception_type":{"type":"string","maxLength":120,"description":"Exact, e.g. TypeError"},"message":{"type":"string","maxLength":240,"description":"Exact; normalized the same way the fingerprint is (uuids, digits and hex collapse)"},"environment":{"type":["string","null"],"maxLength":120,"description":"Omit to apply to every environment"},"frame_url_origin":{"type":["string","null"],"maxLength":120,"description":"Scheme or host prefix, e.g. chrome-extension:// — never a path pattern"},"reason":{"type":"string","minLength":12,"description":"Why this is not your code. Required and preserved on the rule."},"name":{"type":"string","maxLength":240},"expires_at":{"type":["integer","null"],"description":"Millisecond timestamp; must be in the future"},"close_existing":{"type":"boolean","default":true}}}}}},"responses":{"201":{"description":"Created rule and the number of existing issues it closed"},"400":{"description":"Validation or safety-boundary rejection","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/projects/{project_id}/noise/rules/preview":{"post":{"summary":"Preview what a noise rule would cover","description":"Runs the candidate specification against captured history and returns the matched issues, occurrence and affected-user counts, environments, releases, and non-blocking warnings. The mandatory step before committing a rule.","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Match manifest"},"400":{"description":"Validation or safety-boundary rejection","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/projects/{project_id}/noise/rules/{rule_id}":{"get":{"summary":"Read one noise rule with its version history, match series and samples","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"rule_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rule detail"},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}},"patch":{"summary":"Replace a noise rule specification, creating a new immutable version","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"rule_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated rule"},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Disable a noise rule","description":"Forward-only and immediate: matching stops now, previously matched events are not replayed, and issues the rule closed stay closed until the class recurs. Rules are never deleted, so their history stays auditable.","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"rule_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Disabled rule"},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/projects/{project_id}/noise/rules/{rule_id}/enable":{"post":{"summary":"Re-enable a disabled noise rule","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"rule_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Enabled rule"},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/projects/{project_id}/noise/matches":{"get":{"summary":"Read suppressed volume per noise rule","description":"Passive counters only. Matched volume never sends an email, creates an issue, or drives a health rule.","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"days","in":"query","schema":{"type":"integer","minimum":1,"maximum":365,"default":7}}],"responses":{"200":{"description":"Per-rule matched counts over the window"}}}},"/api/projects/{project_id}/event-types":{"get":{"summary":"Suggest retained event types for rule builders","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"q","in":"query","schema":{"type":"string","maxLength":120}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":25,"default":12}}],"responses":{"200":{"description":"Matching event types, usage counts, and exact-match state"}}}},"/api/projects/{project_id}/health":{"get":{"summary":"Read current project health, optionally with evaluation history","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"history_limit","in":"query","description":"Include this many recent evaluations; omitted by default","schema":{"type":"integer","minimum":1,"maximum":500}},{"name":"history_cursor","in":"query","description":"Opaque cursor from history_pagination.nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":"Project health"}}}},"/api/projects/{project_id}/health/rules":{"get":{"summary":"List current health rules and their latest evaluations","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current health rules"}}},"post":{"summary":"Create and immediately evaluate a typed health rule","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthRuleSpecification"}}}},"responses":{"201":{"description":"Created rule version"}}}},"/api/projects/{project_id}/health/rules/{rule_id}":{"get":{"summary":"Get one current health rule and its latest evaluation","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"rule_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current health rule"},"404":{"description":"Rule not found"}}},"patch":{"summary":"Create a new immutable version of a health rule","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"rule_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthRuleSpecification"}}}},"responses":{"200":{"description":"Updated rule version"}}},"delete":{"summary":"Archive a health rule while preserving evaluation history","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"rule_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Archived rule"}}}},"/api/projects/{project_id}/health/evaluate":{"post":{"summary":"Evaluate all current project health rules now","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"New evaluations and aggregate status"}}}},"/api/projects/{project_id}/metrics":{"get":{"summary":"List current metrics and their latest value-only evaluations","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"history_limit","in":"query","description":"Include this many recent evaluations; omitted by default","schema":{"type":"integer","minimum":1,"maximum":500}},{"name":"history_cursor","in":"query","description":"Opaque cursor from history_pagination.nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":"Current metrics"}}},"post":{"summary":"Create and immediately evaluate a status-free metric","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetricSpecification"}}}},"responses":{"201":{"description":"Created metric version"}}}},"/api/projects/{project_id}/metrics/{metric_id}":{"get":{"summary":"Get one current metric and its latest value-only evaluation","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"metric_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current metric"},"404":{"description":"Metric not found"}}},"patch":{"summary":"Create a new immutable version of a metric","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"metric_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetricSpecification"}}}},"responses":{"200":{"description":"Updated metric version"}}},"delete":{"summary":"Archive a metric while preserving evaluation history","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"metric_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Archived metric"}}}},"/api/projects/{project_id}/metrics/evaluate":{"post":{"summary":"Evaluate all current project metrics now","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"New value-only metric evaluations"}}}},"/api/projects/{project_id}/notifications/environments":{"patch":{"summary":"Set a per-environment on/off override of the project notification default","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated notification configuration"}}}},"/api/projects/{project_id}/events":{"get":{"summary":"Query events","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"type","in":"query","schema":{"type":"string"}},{"name":"environment","in":"query","schema":{"type":"string"}},{"name":"release","in":"query","schema":{"type":"string"}},{"name":"source","in":"query","schema":{"type":"string"}},{"name":"identity_id","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated events"}}}},"/api/projects/{project_id}/events/{event_id}/raw":{"get":{"summary":"Fetch raw event payload","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"event_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Raw payload"}}}},"/api/projects/{project_id}/issues":{"get":{"summary":"Query exception issues","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"}},{"name":"environment","in":"query","schema":{"type":"string"}},{"name":"fingerprint","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated issues"}}}},"/api/projects/{project_id}/issues/{issue_id}":{"get":{"summary":"Read an issue with occurrences and comments","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"issue_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Issue detail"}}},"patch":{"summary":"Update issue status or assignment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"issue_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated issue"}}}},"/api/projects/{project_id}/issues/{issue_id}/lifecycle":{"get":{"summary":"Read issue lifecycle history","description":"Returns the current issue, active reactivation rule, status transitions, and notification deliveries.","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"issue_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Issue lifecycle detail"},"404":{"description":"Issue not found","content":{"application/json":{"schema":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/projects/{project_id}/issues/{issue_id}/comments":{"get":{"summary":"List issue comments","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"issue_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated comments"}}},"post":{"summary":"Create an issue comment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"issue_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Created comment"}}}},"/api/projects/{project_id}/issues/{issue_id}/comments/{comment_id}":{"patch":{"summary":"Update an issue comment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"issue_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated comment"}}},"delete":{"summary":"Delete an issue comment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"issue_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted comment"}}}},"/api/projects/{project_id}/export":{"get":{"summary":"Export project data","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"before","in":"query","schema":{"type":"string"}},{"name":"include_raw","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Project export"}}}},"/api/projects/{project_id}/ingestion-health":{"get":{"summary":"Read ingestion health","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Ingestion health"}}}},"/api/projects/{project_id}/retention/run":{"post":{"summary":"Run retention cleanup","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Retention result"}}}}}}