{"openapi":"3.1.0","info":{"title":"SENTRY-API","description":"SENTRY-API public data API for the SENTRY Star Citizen data system. Query citizen and organization profiles, search the database, and retrieve scrape statistics.\n\nOpenAPI JSON: `/api/v1/openapi.json` · Swagger UI: `/api/v1/docs`","contact":{"name":"SENTRY / Wild Knight Squadron"},"license":{"name":"MIT"},"version":"1.0.0"},"paths":{"/api/v1/stats":{"get":{"tags":["Stats"],"summary":"Get scrape and database statistics","description":"Return aggregate statistics: total citizens/orgs/memberships, queue depth, completed/failed/pending fetch counts, a 24-hour hourly timechart, and 30-day new/updated citizen and organization series. Served from a precomputed Redis cache refreshed every 15 minutes.","operationId":"get_stats","parameters":[{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp filter (reserved; currently unused)","title":"Timestamp"},"description":"Optional ISO timestamp filter (reserved; currently unused)"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Optional max age in seconds; must not be negative if provided","title":"Max Age"},"description":"Optional max age in seconds; must not be negative if provided"}],"responses":{"200":{"description":"Statistics payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsResponse"}}}},"400":{"description":"Invalid parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Stats cache not ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/citizens/":{"get":{"tags":["citizens"],"summary":"List recently scraped citizens","description":"Return the most recently scraped citizens (typically the last 10). Useful for dashboards and activity feeds.","operationId":"list_citizens","parameters":[{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp filter (reserved; currently unused by this list endpoint)","title":"Timestamp"},"description":"Optional ISO timestamp filter (reserved; currently unused by this list endpoint)"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Optional max age in seconds (reserved; currently unused by this list endpoint)","title":"Max Age"},"description":"Optional max age in seconds (reserved; currently unused by this list endpoint)"}],"responses":{"200":{"description":"Recent citizens","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CitizenSummary"},"title":"Response List Citizens"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/citizens/{handle}":{"get":{"tags":["citizens"],"summary":"Get citizen profile","description":"Retrieve a citizen's full profile by RSI handle, including memberships, membership history, field changes, and scrape metadata. If the citizen is missing or a stub, the API may trigger a synchronous scrape and/or queue background refresh work. Results are cached in Redis.","operationId":"get_citizen","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","description":"RSI citizen handle (case-insensitive)","title":"Handle"},"description":"RSI citizen handle (case-insensitive)"},{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp for historical point-in-time profile/membership data","title":"Timestamp"},"description":"Optional ISO timestamp for historical point-in-time profile/membership data"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Maximum acceptable data age in seconds before a background refresh is queued","title":"Max Age"},"description":"Maximum acceptable data age in seconds before a background refresh is queued"}],"responses":{"200":{"description":"Citizen data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitizenResponse"}}}},"400":{"description":"Invalid handle","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Citizen not found on RSI","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/citizens/{handle}/{sub_resource}":{"get":{"tags":["citizens"],"summary":"Get citizen sub-resource","description":"Return one top-level section of a citizen payload. Valid sub_resource values: `profile`, `memberships`, `membership_history`, `changes`, `metadata`.","operationId":"get_citizen_subresource","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","description":"RSI citizen handle","title":"Handle"},"description":"RSI citizen handle"},{"name":"sub_resource","in":"path","required":true,"schema":{"type":"string","description":"One of: profile, memberships, membership_history, changes, metadata","title":"Sub Resource"},"description":"One of: profile, memberships, membership_history, changes, metadata"},{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp for historical data","title":"Timestamp"},"description":"Optional ISO timestamp for historical data"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Max acceptable data age in seconds","title":"Max Age"},"description":"Max acceptable data age in seconds"}],"responses":{"200":{"description":"Sub-resource payload (object or array depending on section)","content":{"application/json":{"schema":{"title":"Response Get Citizen Subresource"}}}},"404":{"description":"Citizen or sub-resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/citizens/{handle}/{sub_resource}/{field}":{"get":{"tags":["citizens"],"summary":"Get a field from a citizen sub-resource","description":"Return a single field from a citizen sub-resource object (e.g. `/citizens/{handle}/profile/display_name`).","operationId":"get_citizen_field","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","description":"RSI citizen handle","title":"Handle"},"description":"RSI citizen handle"},{"name":"sub_resource","in":"path","required":true,"schema":{"type":"string","description":"Sub-resource name, e.g. profile or metadata","title":"Sub Resource"},"description":"Sub-resource name, e.g. profile or metadata"},{"name":"field","in":"path","required":true,"schema":{"type":"string","description":"Field name within the sub-resource","title":"Field"},"description":"Field name within the sub-resource"},{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp for historical data","title":"Timestamp"},"description":"Optional ISO timestamp for historical data"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Max acceptable data age in seconds","title":"Max Age"},"description":"Max acceptable data age in seconds"}],"responses":{"200":{"description":"Field value (type depends on the field)","content":{"application/json":{"schema":{"title":"Response Get Citizen Field"}}}},"404":{"description":"Citizen, sub-resource, or field not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/orgs/":{"get":{"tags":["orgs"],"summary":"List recently scraped organizations","description":"Return the most recently scraped organizations (typically the last 10). Useful for dashboards and activity feeds.","operationId":"list_organizations","parameters":[{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp filter (reserved; currently unused by this list endpoint)","title":"Timestamp"},"description":"Optional ISO timestamp filter (reserved; currently unused by this list endpoint)"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Optional max age in seconds (reserved; currently unused by this list endpoint)","title":"Max Age"},"description":"Optional max age in seconds (reserved; currently unused by this list endpoint)"}],"responses":{"200":{"description":"Recent organizations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationSummary"},"title":"Response List Organizations"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/orgs/{sid}":{"get":{"tags":["orgs"],"summary":"Get organization profile","description":"Retrieve an organization's full profile by Spectrum ID (SID), including paginated memberships, field changes, and scrape metadata. Missing/stub orgs may trigger a synchronous profile scrape; member pages are typically queued for workers. Results are cached in Redis.","operationId":"get_organization","parameters":[{"name":"sid","in":"path","required":true,"schema":{"type":"string","description":"Organization Spectrum ID / SID (case-insensitive)","title":"Sid"},"description":"Organization Spectrum ID / SID (case-insensitive)"},{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp for historical point-in-time profile/membership data","title":"Timestamp"},"description":"Optional ISO timestamp for historical point-in-time profile/membership data"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Maximum acceptable data age in seconds before a background refresh is queued","title":"Max Age"},"description":"Maximum acceptable data age in seconds before a background refresh is queued"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Memberships page number (1-based)","default":1,"title":"Page"},"description":"Memberships page number (1-based)"},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Memberships per page","default":50,"title":"Per Page"},"description":"Memberships per page"}],"responses":{"200":{"description":"Organization data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponse"}}}},"400":{"description":"Invalid SID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Organization not found on RSI","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/orgs/{sid}/membership_history":{"get":{"tags":["orgs"],"summary":"Get organization join/leave history","description":"Computed join/leave history for an organization SID. Join is the first time SENTRY observed the citizen in this org; leave is the last observation after a citizen scrape no longer lists the org. Org roster pages are paged and do not record leaves by themselves.","operationId":"get_organization_membership_history","parameters":[{"name":"sid","in":"path","required":true,"schema":{"type":"string","description":"Organization Spectrum ID / SID (case-insensitive)","title":"Sid"},"description":"Organization Spectrum ID / SID (case-insensitive)"},{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp; only observations at or before this time are used","title":"Timestamp"},"description":"Optional ISO timestamp; only observations at or before this time are used"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"History page number (1-based)","default":1,"title":"Page"},"description":"History page number (1-based)"},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Events per page","default":50,"title":"Per Page"},"description":"Events per page"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter: all, active (still members), or left","default":"all","title":"Status"},"description":"Filter: all, active (still members), or left"}],"responses":{"200":{"description":"Paginated join/leave history","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMembershipHistoryPage"}}}},"400":{"description":"Invalid SID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/orgs/{sid}/{sub_resource}":{"get":{"tags":["orgs"],"summary":"Get organization sub-resource","description":"Return one top-level section of an organization payload. Valid sub_resource values: `profile`, `memberships`, `changes`, `metadata`, `membership_history`.","operationId":"get_organization_subresource","parameters":[{"name":"sid","in":"path","required":true,"schema":{"type":"string","description":"Organization SID","title":"Sid"},"description":"Organization SID"},{"name":"sub_resource","in":"path","required":true,"schema":{"type":"string","description":"One of: profile, memberships, changes, metadata","title":"Sub Resource"},"description":"One of: profile, memberships, changes, metadata"},{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp for historical data","title":"Timestamp"},"description":"Optional ISO timestamp for historical data"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Max acceptable data age in seconds","title":"Max Age"},"description":"Max acceptable data age in seconds"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Memberships page number","default":1,"title":"Page"},"description":"Memberships page number"},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Memberships per page","default":50,"title":"Per Page"},"description":"Memberships per page"}],"responses":{"200":{"description":"Sub-resource payload (object or array depending on section)","content":{"application/json":{"schema":{"title":"Response Get Organization Subresource"}}}},"404":{"description":"Organization or sub-resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/orgs/{sid}/{sub_resource}/{field}":{"get":{"tags":["orgs"],"summary":"Get a field from an organization sub-resource","description":"Return a single field from an organization sub-resource object (e.g. `/orgs/{sid}/profile/member_count`).","operationId":"get_organization_field","parameters":[{"name":"sid","in":"path","required":true,"schema":{"type":"string","description":"Organization SID","title":"Sid"},"description":"Organization SID"},{"name":"sub_resource","in":"path","required":true,"schema":{"type":"string","description":"Sub-resource name, e.g. profile or metadata","title":"Sub Resource"},"description":"Sub-resource name, e.g. profile or metadata"},{"name":"field","in":"path","required":true,"schema":{"type":"string","description":"Field name within the sub-resource","title":"Field"},"description":"Field name within the sub-resource"},{"name":"timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ISO timestamp for historical data","title":"Timestamp"},"description":"Optional ISO timestamp for historical data"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Max acceptable data age in seconds","title":"Max Age"},"description":"Max acceptable data age in seconds"}],"responses":{"200":{"description":"Field value (type depends on the field)","content":{"application/json":{"schema":{"title":"Response Get Organization Field"}}}},"404":{"description":"Organization, sub-resource, or field not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/search":{"get":{"tags":["search"],"summary":"Search citizens and organizations","description":"Fuzzy-search citizens (handle/display name) and organizations (SID/name) using Postgres trigram similarity. Exact matches may trigger a live scrape if missing. When a scrape is still running, results may include pending placeholders and `finalized` will be false. Optional `type` limits the search to citizens or organizations; omit it to keep the original combined search (legacy clients).","operationId":"search_citizens_and_organizations","parameters":[{"name":"query","in":"query","required":true,"schema":{"type":"string","minLength":2,"description":"Search query (minimum 2 characters). Matches citizen handles/names and org SIDs/names.","title":"Query"},"description":"Search query (minimum 2 characters). Matches citizen handles/names and org SIDs/names."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"enum":["citizen","organization"],"type":"string"},{"type":"null"}],"description":"Optional. Limit results to citizens or organizations. Omit to search both (legacy default). Does not change response shape.","title":"Type"},"description":"Optional. Limit results to citizens or organizations. Omit to search both (legacy default). Does not change response shape."}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"ChangeRecord":{"properties":{"field_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Field Name"},"old_value":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Old Value"},"new_value":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Value"},"timestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Timestamp"}},"additionalProperties":true,"type":"object","title":"ChangeRecord","description":"A single field change recorded for a citizen or organization."},"CitizenProfile":{"properties":{"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle","description":"RSI handle (lowercase)"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"citizen_record":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Citizen Record"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"enlisted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Enlisted"},"fluency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fluency"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website"},"probation":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Probation"},"probation_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Probation End"},"is_stub":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Stub","description":"True for placeholder profiles"}},"additionalProperties":true,"type":"object","title":"CitizenProfile","description":"Citizen profile fields from RSI / local DB."},"CitizenResponse":{"properties":{"profile":{"$ref":"#/components/schemas/CitizenProfile"},"memberships":{"items":{"$ref":"#/components/schemas/MembershipRecord"},"type":"array","title":"Memberships"},"membership_history":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Membership History","description":"Historical membership snapshots"},"changes":{"items":{"$ref":"#/components/schemas/ChangeRecord"},"type":"array","title":"Changes"},"metadata":{"$ref":"#/components/schemas/ResourceMetadata"}},"additionalProperties":true,"type":"object","required":["profile"],"title":"CitizenResponse","description":"Full citizen payload returned by GET /citizens/{handle}."},"CitizenSummary":{"properties":{"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"last_scraped":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Scraped"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"additionalProperties":true,"type":"object","title":"CitizenSummary","description":"Compact citizen row (e.g. recent list)."},"ErrorResponse":{"properties":{"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail","description":"Human-readable error detail"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message (worker-style responses)"}},"type":"object","title":"ErrorResponse","description":"Standard error payload returned by many endpoints."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"MembershipRecord":{"properties":{"citizen_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Citizen Handle"},"organization_sid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Organization Sid"},"member_rank":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Member Rank"},"rank_number":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rank Number"},"is_main":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Main"},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility","description":"RSI membership visibility: 'visible', 'hidden', or 'redacted'. Hidden/redacted memberships use placeholder SIDs [HIDDEN] / [REDACTED] (and :N suffixes) so they never collide with the real orgs HIDDEN and REDACTED."}},"additionalProperties":true,"type":"object","title":"MembershipRecord","description":"Citizen ↔ organization membership."},"OrgMembershipHistoryEvent":{"properties":{"citizen_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Citizen Handle"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"join_detected":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Join Detected","description":"First time SENTRY observed this membership"},"leave_detected":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Leave Detected","description":"Last observation when the citizen is no longer a current member"},"status":{"anyOf":[{"type":"string","enum":["active","left"]},{"type":"null"}],"title":"Status"},"is_main":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Main"},"current_rank":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Rank"},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility"}},"additionalProperties":true,"type":"object","title":"OrgMembershipHistoryEvent","description":"One computed join/leave observation for an organization."},"OrgMembershipHistoryPage":{"properties":{"organization_sid":{"type":"string","title":"Organization Sid"},"events":{"items":{"$ref":"#/components/schemas/OrgMembershipHistoryEvent"},"type":"array","title":"Events"},"page":{"type":"integer","title":"Page","default":1},"pages":{"type":"integer","title":"Pages","default":1},"total":{"type":"integer","title":"Total","description":"Events matching the status filter","default":0},"active":{"type":"integer","title":"Active","description":"Current members (unfiltered)","default":0},"left":{"type":"integer","title":"Left","description":"Detected leavers (unfiltered)","default":0},"status":{"type":"string","title":"Status","description":"Filter applied: all, active, or left","default":"all"}},"type":"object","required":["organization_sid"],"title":"OrgMembershipHistoryPage","description":"Paginated join/leave history for an organization."},"OrgMembershipsPage":{"properties":{"members":{"items":{"$ref":"#/components/schemas/MembershipRecord"},"type":"array","title":"Members"},"page":{"type":"integer","title":"Page","default":1},"pages":{"type":"integer","title":"Pages","default":1},"total":{"type":"integer","title":"Total","description":"Reported member_count from org profile","default":0},"fetched":{"type":"integer","title":"Fetched","description":"Number of memberships currently in the DB","default":0}},"type":"object","title":"OrgMembershipsPage","description":"Paginated organization memberships."},"OrganizationProfile":{"properties":{"sid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sid","description":"Organization Spectrum ID (uppercase)"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"history":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"History"},"manifesto":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manifesto"},"charter":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Charter"},"member_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Member Count"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"is_stub":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Stub","description":"True for placeholder profiles"}},"additionalProperties":true,"type":"object","title":"OrganizationProfile","description":"Organization profile fields from RSI / local DB."},"OrganizationResponse":{"properties":{"profile":{"$ref":"#/components/schemas/OrganizationProfile"},"memberships":{"$ref":"#/components/schemas/OrgMembershipsPage"},"changes":{"items":{"$ref":"#/components/schemas/ChangeRecord"},"type":"array","title":"Changes"},"metadata":{"$ref":"#/components/schemas/ResourceMetadata"}},"additionalProperties":true,"type":"object","required":["profile"],"title":"OrganizationResponse","description":"Full organization payload returned by GET /orgs/{sid}."},"OrganizationSummary":{"properties":{"sid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sid"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"member_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Member Count"},"last_scraped":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Scraped"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"additionalProperties":true,"type":"object","title":"OrganizationSummary","description":"Compact organization row (e.g. recent list)."},"ResourceMetadata":{"properties":{"first_added":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"First Added","description":"When this record was first added to the SENTRY database"},"last_fetched":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Fetched","description":"Last successful scrape time"},"is_stub":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Stub","description":"True if only a placeholder record exists"},"is_archived":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Archived","description":"True if resource appears gone from RSI"},"finalized":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Finalized","description":"True when no pending/in-progress scrape tasks remain for this resource"},"force_refreshed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Force Refreshed","description":"Present when an internal force-refresh scrape was performed"}},"additionalProperties":true,"type":"object","title":"ResourceMetadata","description":"Scrape/metadata state for a citizen or organization."},"SearchResponse":{"properties":{"finalized":{"type":"boolean","title":"Finalized","description":"False while exact-match scrape tasks are still pending"},"results":{"items":{"$ref":"#/components/schemas/SearchResultItem"},"type":"array","title":"Results"},"pending_tasks":{"items":{"type":"string"},"type":"array","title":"Pending Tasks","description":"Which exact-match types still have pending work: 'citizen' and/or 'organization'"}},"type":"object","required":["finalized"],"title":"SearchResponse","description":"Search results for citizens and/or organizations.\n\nOptional query param `type=citizen` or `type=organization` filters server-side.\nOmitting `type` keeps the original combined search (legacy)."},"SearchResultItem":{"properties":{"type":{"type":"string","enum":["citizen","organization"],"title":"Type"},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle","description":"Citizen handle (when type=citizen)"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"sid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sid","description":"Org SID (when type=organization)"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"last_fetched":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Fetched"},"first_added":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"First Added","description":"When this record was first added to the SENTRY database"},"is_stub":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Stub"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"'pending' while a scrape is in flight"},"sim":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sim","description":"Trigram similarity score for partial matches"}},"additionalProperties":true,"type":"object","required":["type"],"title":"SearchResultItem","description":"One search hit (citizen or organization). Extra fields allowed."},"StatsDaySeries":{"properties":{"days":{"items":{"type":"string"},"type":"array","title":"Days","description":"YYYY-MM-DD"},"counts":{"items":{"type":"integer"},"type":"array","title":"Counts"}},"type":"object","title":"StatsDaySeries"},"StatsResponse":{"properties":{"totals":{"$ref":"#/components/schemas/StatsTotals"},"completed_fetches":{"$ref":"#/components/schemas/StatsWindowCounts"},"failed_fetches":{"$ref":"#/components/schemas/StatsWindowCounts"},"pending_fetches":{"$ref":"#/components/schemas/StatsWindowCounts"},"timechart":{"$ref":"#/components/schemas/StatsTimechart"},"new_citizens_30d":{"anyOf":[{"$ref":"#/components/schemas/StatsDaySeries"},{"type":"null"}]},"new_orgs_30d":{"anyOf":[{"$ref":"#/components/schemas/StatsDaySeries"},{"type":"null"}]},"updated_citizens_30d":{"anyOf":[{"$ref":"#/components/schemas/StatsDaySeries"},{"type":"null"}]},"updated_orgs_30d":{"anyOf":[{"$ref":"#/components/schemas/StatsDaySeries"},{"type":"null"}]},"new_citizens_30d_raw":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"New Citizens 30D Raw"},"new_orgs_30d_raw":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"New Orgs 30D Raw"},"total_citizens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Citizens","description":"Legacy alias of totals.citizens"},"total_organizations":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Organizations","description":"Legacy alias of totals.organizations"},"total_memberships":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Memberships","description":"Legacy alias of totals.memberships"},"pending_tasks":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pending Tasks"},"in_progress_tasks":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"In Progress Tasks"},"completed_tasks":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Completed Tasks"}},"additionalProperties":true,"type":"object","title":"StatsResponse","description":"Database and scrape-queue statistics."},"StatsTimechart":{"properties":{"hours":{"items":{"type":"string"},"type":"array","title":"Hours","description":"ISO hour buckets (last 24h)"},"completed":{"items":{"type":"integer"},"type":"array","title":"Completed"},"failed":{"items":{"type":"integer"},"type":"array","title":"Failed"},"pending":{"items":{"type":"integer"},"type":"array","title":"Pending"}},"type":"object","title":"StatsTimechart"},"StatsTotals":{"properties":{"citizens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Citizens"},"organizations":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Organizations"},"memberships":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Memberships"},"queue":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Queue","description":"Pending + in-progress scrape tasks"}},"type":"object","title":"StatsTotals"},"StatsWindowCounts":{"properties":{"last_hour":{"type":"integer","title":"Last Hour","default":0},"last_24_hours":{"type":"integer","title":"Last 24 Hours","default":0}},"type":"object","title":"StatsWindowCounts"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"citizens","description":"Query Star Citizen player (citizen) profiles, memberships, and change history."},{"name":"orgs","description":"Query organization profiles, members, and change history."},{"name":"search","description":"Fuzzy search across citizens and organizations."},{"name":"Stats","description":"Database and scrape-queue statistics."}]}