{"openapi":"3.0.0","paths":{"/auth/login":{"post":{"description":"Signs in a full user and returns an access token, a refresh token and the user's profile with their organizations, and creates a session. Wrong credentials, an inactive account or an extraction-only user get 401. Each organization entry carries the caller's permission and the org's billing summary.","operationId":"AuthController_login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["accessToken","refreshToken","userProfile"],"properties":{"accessToken":{"type":"string","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NWExYjJjMyJ9.c2lnbmF0dXJl"},"refreshToken":{"type":"string","example":"b7c1e2f0a9d84c3e8f6a5b4c3d2e1f00"},"userProfile":{"$ref":"#/components/schemas/UserEntity"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Sign in with email and password","tags":["auth"]}},"/auth/generate-token":{"post":{"description":"With `scopes`, mints a scoped automation token (lifetime from `ttlDays` or the configured default, 365 days when unset, never more than 400) and returns the token once with its id, label, scopes and expiry; returns 403 while automation tokens are turned off and 400 for an empty or unsupported scope list. Without `scopes`, mints a legacy full-access token valid for 5 years and returns only `token` and `validTill` (no id); sending `label` or `ttlDays` without `scopes` returns 400. An inactive user gets 401.","operationId":"AuthController_generateToken","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateTokenDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/GenerateTokenResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a long-lived API token","tags":["auth"]}},"/auth/tokens":{"get":{"description":"Lists the caller's scoped automation tokens, newest first, with label, scopes, last use, expiry and revoked flag. Token values are never returned, and legacy tokens are not listed.","operationId":"AuthController_listTokens","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AutomationTokenListEntity"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List my automation tokens","tags":["auth"]}},"/auth/tokens/{id}":{"delete":{"description":"Marks one of the caller's automation tokens as revoked. Works even while automation tokens are turned off. Returns 404 when the id is not one of the caller's automation tokens.","operationId":"AuthController_revokeToken","parameters":[{"name":"id","required":true,"in":"path","description":"Automation token (session) id","schema":{"type":"string"}}],"responses":{"204":{"description":"Token revoked. No body."}},"security":[{"access-token":[]}],"summary":"Revoke an automation token","tags":["auth"]}},"/auth/tokens/{id}/scopes":{"patch":{"description":"Replaces the scopes of one of the caller's automation tokens (duplicates removed; an empty list disables the token without deleting it) and returns the updated token. Returns 403 while automation tokens are turned off, 400 for an unsupported scope and 404 when the id is not one of the caller's automation tokens.","operationId":"AuthController_updateTokenScopes","parameters":[{"name":"id","required":true,"in":"path","description":"Automation token (session) id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTokenScopesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AutomationTokenListEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Change an automation token's scopes","tags":["auth"]}},"/auth/refresh":{"post":{"description":"Issues a new access token for a refresh token and returns it with the same refresh token. The user profile is not included. Returns 404 for an unknown refresh token and 400 when it has expired or its user no longer exists.","operationId":"AuthController_refresh","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RefreshTokenResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Refresh the access token","tags":["auth"]}},"/auth/logout":{"post":{"description":"Ends the current session by deleting the session of the bearer token. `data` is always an empty object.","operationId":"AuthController_logout","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"Always empty","properties":{}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Sign out","tags":["auth"]}},"/auth/register":{"post":{"description":"Creates the user and, unless they already belong to one, an organization named from `organizationName` (or from the user's name) with the user as owner, invites `inviteEmails`, records acceptance of the terms, then signs the user in. Returns access and refresh tokens, the user's profile and the organization. Returns 400 when the terms are not accepted, the organization name is taken or the user cannot be created. Each organization entry carries the caller's permission and the org's billing summary.","operationId":"AuthController_register","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["accessToken","refreshToken","userProfile"],"properties":{"accessToken":{"type":"string","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NWExYjJjMyJ9.c2lnbmF0dXJl"},"refreshToken":{"type":"string","example":"b7c1e2f0a9d84c3e8f6a5b4c3d2e1f00"},"userProfile":{"$ref":"#/components/schemas/UserEntity"},"organizationInfo":{"$ref":"#/components/schemas/OrganizationDetailsEntity"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Sign up a new user and organization","tags":["auth"]}},"/auth/send-forgot-password-link":{"post":{"description":"Emails a password-reset link when the address belongs to a user. Always answers 200: an unknown email comes back with `isSuccess: false` and the error message instead of an error status.","operationId":"AuthController_sendForgotPasswordLink","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendForgotPasswordLinkDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Email a password-reset link","tags":["auth"]}},"/auth/reset-password":{"post":{"description":"Sets a new password using the emailed reset token and the email, then sends a password-changed email. Always answers 200: an unknown or expired token comes back with `isSuccess: false` and the error message.","operationId":"AuthController_resetPassword","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Reset the password with an emailed token","tags":["auth"]}},"/auth/check-email-availability":{"post":{"description":"Says whether an email is free to sign up with. `isAvailable` is false when it belongs to a user (`type: 'user'`) or to a custodian on an extraction code (`type: 'custodian'`); `type` is null when it is available. No auth; limited to 10 requests per minute.","operationId":"AuthController_checkEmailAvailability","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckEmailAvailabilityDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/EmailAvailabilityResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Check whether an email can sign up","tags":["auth"]}},"/cases/{caseId}/pro-upgrade":{"post":{"description":"Records a one-time Pro upgrade charge for the case and returns `status: 'processing'`; the case becomes Pro once the invoice is paid, or right away when the plan already includes Pro, and repeating the request does not charge again. Requires a member role or above in the organization, owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel. Returns 403 when case billing is not enabled for the organization or the case belongs to a different organization than the `organization` header, and 409 when the case is already Pro.","operationId":"BillingController_requestCaseProUpgrade","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseProUpgradeResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Upgrade a case to Pro","tags":["billing"]}},"/billing/setup-session":{"post":{"description":"Creates a Stripe Checkout session in setup mode that puts a card on file without charging it, and returns its `url`; Stripe sends the user back to `successUrl` or `cancelUrl`. Requires an org admin, owner or billing role. Returns 403 when case billing is not enabled for the organization, and 409 when its billing profile has no Stripe customer yet.","operationId":"BillingController_createSetupSession","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBillingSetupSessionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BillingSetupSessionResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start adding a card","tags":["billing"]}},"/billing/plan":{"post":{"description":"Puts the organization on a self-serve tier in pay-as-you-go mode, or changes its tier or cadence (monthly by default, annual for an annual-only tier); a change settles the current base fee at once and returns the net amount in `prorationCents` (0 on a first selection), and a first selection also moves the organization off any legacy subscription. When no card is on file, `paymentRequired` is true and `setupSessionUrl` links to a card setup page, so `successUrl` and `cancelUrl` are then required (400 otherwise). Requires an org admin, owner or billing role; returns 403 while self-serve plan selection is not available and 400 for a monthly cadence on an annual-only tier.","operationId":"BillingController_selectPlan","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelectPlanDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/SelectPlanResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Choose or change the plan","tags":["billing"]}},"/cases/{caseId}/billing/statement":{"get":{"description":"Lists the case's billing rows newest first (50 by default, at most 200, paged with `limit` and `skip`) with the total charged and the list value of plan-included rows; both totals cover every row, not just the page. Plan-included rows have `informational: true` and, like waived or voided rows, never count toward `totalChargedCents`. Requires a member role or above in the organization and owner or admin access to the case; not available to opposing counsel.","operationId":"BillingStatementController_getCaseStatement","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"type":"number"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseStatementResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a case's billing statement","tags":["billing"]}},"/billing/reconciliation":{"get":{"description":"Lists the organization's billing rows newest first (50 by default, at most 200, paged with `limit` and `skip`), grouped by case; org-wide charges such as the plan base fee come in a group with `caseId: 'organization'` and `chargeScope: 'platform'`. Each group's `subtotalChargedCents` covers all of its rows, not just the page, and each row carries its Stripe invoice id and hosted invoice link once invoiced. Requires an org admin, owner or billing role.","operationId":"BillingStatementController_getOrgReconciliation","parameters":[{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"type":"number"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrgReconciliationResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the organization's charges by case","tags":["billing"]}},"/billing/history":{"get":{"description":"Lists the organization's billing events (subscription, invoicing, payment method, case billing and admin actions) newest first, filterable by `category` and `caseId`; 50 by default, at most 200, paged with `limit` and `skip`. Requires an org admin, owner or billing role. Adjustments made by Hearsay staff show a neutral description, no reason and no `createdBy`.","operationId":"BillingStatementController_getOrgHistory","parameters":[{"name":"category","required":false,"in":"query","schema":{"type":"string","enum":["subscription","invoicing","payment_method","case_billing","admin"]}},{"name":"caseId","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"type":"number"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingHistoryResponseEntity"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List billing events (internal)","tags":["billing"],"x-internal":true}},"/billing/plans":{"get":{"description":"Lists the self-serve tiers with monthly and annual base price in cents, seat and storage caps, included features and allowed cadences. The organization's own plan is on `GET /billing/plan/current`. Requires an org admin, owner or billing role.","operationId":"BillingStatementController_getPlans","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BillingPlansResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List self-serve plans","tags":["billing"]}},"/billing/plan/current":{"get":{"description":"Returns the organization's plan (tier, cadence, billing mode and the card that will be charged) with usage against its caps for seats, storage, this month's AI tokens and cases, and `delinquent`, which is true when a plan base fee failed more than 7 days ago. `plan` is null when the organization is not on case billing; usage is still returned. Requires an org admin, owner or billing role.","operationId":"BillingStatementController_getCurrentPlan","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BillingPlanCurrentResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the current plan and usage","tags":["billing"]}},"/billing/credits":{"get":{"description":"Returns the organization's pre-purchased credit balance in cents, its currency and the share of each invoice the balance covers (`effectiveDeductPercentage`), with the credit ledger newest first (50 entries by default, paged with `limit` and `skip`). Requires an org admin, owner or billing role; returns 403 while credits are turned off.","operationId":"BillingStatementController_getCredits","parameters":[{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"type":"number"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CreditBalanceResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the credit balance","tags":["billing"]}},"/billing/report":{"post":{"description":"Returns the organization's billing charges in a date window, grouped by case, with charged, plan-included, waived or voided, failed and refunded totals; the window defaults to the current billing period up to now and can be limited to some cases. With `format: 'pdf'` it renders the report as a PDF and returns the stored file and a time-limited download URL instead of the data. Requires an org admin, owner or billing role; returns 404 while billing reports are turned off and 400 for a start date after the end date or more than 50,000 rows.","operationId":"BillingReportController_getReport","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingReportQueryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/BillingReportResponseEntity"},{"$ref":"#/components/schemas/BillingReportFileResponseEntity"}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the billing report (internal)","tags":["billing"],"x-internal":true}},"/user/profile":{"get":{"description":"Returns the signed-in user's profile with every organization they belong to. Each organization entry carries the caller's permission and the org's billing summary. The profile can come from a cache up to 60 seconds old.","operationId":"UserController_getProfile","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get my profile","tags":["user"]},"patch":{"description":"Updates the caller's own name, phone numbers, onboarding status or step, or user type, and returns the freshly built profile. Each organization entry carries the caller's permission and the org's billing summary.","operationId":"UserController_updateProfile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update my profile","tags":["user"]}},"/user/change-password":{"post":{"description":"Changes the caller's password after checking the old one and returns the freshly built profile. Returns 400 when the old password is wrong.","operationId":"UserController_changePassword","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Change my password","tags":["user"]}},"/user/verify-email":{"post":{"description":"Marks a user's email as verified using the token from the verification email, optionally matched with `email`. No auth. Returns 404 for an unknown token and 400 when it has expired. `data` holds only `message`.","operationId":"UserController_verifyEmail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["message"],"properties":{"message":{"type":"string","example":"Email verified"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Verify an email address","tags":["user"]}},"/user/resend-verification-email":{"post":{"description":"Sends the caller a new email-verification link. Returns 400 when the email is already verified. Limited to 3 requests per minute. `data` holds only `message`.","operationId":"UserController_resendVerificationEmail","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["message"],"properties":{"message":{"type":"string","example":"Verification email sent"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Resend the verification email (internal)","tags":["user"],"x-internal":true}},"/organization":{"post":{"description":"Creates an organization with the caller as owner, attaches the free plan, sets up its billing customer and invites `inviteEmails` when given. Returns the organization, the caller's permission and its billing summary. Returns 400 when the name is already taken.","operationId":"OrganizationController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationDetailsResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create an organization","tags":["organizations"]}},"/organization/invitations":{"get":{"description":"Lists invitations of every organization the caller belongs to, or of the one in the `organization` query (403 when the caller is not a member of it), filterable by text, status and role; paginated, most recently updated first. Inviting, invited and accepting users and the organization come as objects. `data` is null, not an empty array, when nothing matches.","operationId":"OrganizationController_searchInvitations","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"organization","required":true,"in":"header","description":"id of the selected (active) organization","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["pending","accepted","rejected"]}},{"name":"userRole","required":false,"in":"query","schema":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"]}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"updatedAt","type":"string","enum":["createdAt","updatedAt","email"]}},{"name":"organization","required":false,"in":"query","description":"Limit to one of your organizations (defaults to all of them)","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/InvitationListEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List organization invitations","tags":["organizations"]}},"/organization/validate-name":{"post":{"description":"Returns `isValid: false` when any organization already uses the name (case-insensitive). No auth.","operationId":"OrganizationController_validateName","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateOrganizationNameDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationNameValidateResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Check whether an organization name is free (internal)","tags":["organizations"],"x-internal":true}},"/organization/pending-invitations":{"get":{"description":"Lists the caller's own pending organization invitations, optionally narrowed to one organization or role; paginated, most recently updated first. `data` is null, not an empty array, when nothing matches.","operationId":"OrganizationController_searchPendingInvitations","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"organization","required":false,"in":"query","schema":{"type":"string"}},{"name":"userRole","required":false,"in":"query","schema":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"]}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"updatedAt","type":"string","enum":["createdAt","updatedAt","email"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/InvitationListEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List my pending organization invitations (internal)","tags":["organizations"],"x-internal":true}},"/organization/{orgId}":{"get":{"description":"Returns the organization, the caller's permission in it and its billing summary (`billing` is null for a legacy-billed org). `organization.activePlan` is not populated on this route. Requires a role in the organization (403 otherwise).","operationId":"OrganizationController_getOrgDetails","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationDetailsResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization","tags":["organizations"]},"patch":{"description":"Updates organization fields (name, address, metadata, communication preferences, Plaid terms acceptance and similar) and returns the updated details. Renaming also regenerates the slug; returns 400 when the new name is taken. Requires an org admin or owner and an active plan (403 otherwise).","operationId":"OrganizationController_updateOrgDetails","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationDetailsResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update an organization","tags":["organizations"]}},"/organization/slug/{slug}":{"get":{"description":"Same as getting an organization by id, looked up by its slug. Requires a role in the organization (403 otherwise). An unknown slug also gets 403.","operationId":"OrganizationController_getOrgDetailsBySlug","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationDetailsResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization by slug (internal)","tags":["organizations"],"x-internal":true}},"/organization/{orgId}/usages-stat":{"get":{"description":"Returns the org's billing model, whether its plan is active and paid, seat, device and storage allowances and usage (storage also in KB/MB/GB/TB), whether the caller may manage billing, and whether the device in `deviceId`/`deviceType` is new to the org's cases. Requires a role in the organization (403 otherwise). Returns 400 when the organization has no active plan record.","operationId":"OrganizationController_getOrgUsagesStat","parameters":[{"name":"orgId","required":true,"in":"path","schema":{"type":"string"}},{"name":"deviceId","required":false,"in":"query","schema":{"type":"string"}},{"name":"deviceType","required":false,"in":"query","schema":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationUsagesStatEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization's plan usage (internal)","tags":["organizations"],"x-internal":true}},"/organization/{orgId}/storage-stats":{"get":{"description":"Returns the total storage used by the organization's cases and a per-case breakdown, largest first, counting only cases that are not archived or deleted, optionally limited to `caseIds`. Requires a role in the organization (403 otherwise).","operationId":"OrganizationController_getOrgStorageStats","parameters":[{"name":"orgId","required":true,"in":"path","schema":{"type":"string"}},{"name":"caseIds","required":false,"in":"query","description":"Filter by case ids","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationStorageStatsEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization's storage by case","tags":["organizations"]}},"/organization/user/organizations":{"get":{"description":"Lists every organization the caller belongs to in a single page, each with the caller's permission and the billing summary; `organization.activePlan` is sent without its subscription (null) or payments.","operationId":"OrganizationController_getOrganizationListForUser","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationListEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List my organizations","tags":["organizations"]}},"/organization/{orgId}/members":{"get":{"description":"Lists the organization's members with their role, last activity and user details, filterable by name or email text and by role; paginated, 10 per page by default. Requires a role in the organization (403 otherwise). `data` is null, not an empty array, when nothing matches.","operationId":"OrganizationController_getOrganizationMembers","parameters":[{"name":"orgId","required":true,"in":"path","description":"The id of the organization","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"role","required":false,"in":"query","schema":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"]}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"createdAt","type":"string","enum":["createdAt"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationMembersListEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List organization members","tags":["organizations"]}},"/organization/{orgId}/invite":{"post":{"description":"Invites each email, as `member` unless a role is given. Existing members are skipped and a pending invitation is sent again; when auto-accept is on, existing users are added directly. Requires a member role or above in the organization and an active plan (403 otherwise). A plain member may only invite clients or members (401 otherwise), and 403 is returned when the plan's seat limit is reached.","operationId":"OrganizationController_inviteUsers","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Invitation request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteOrganizationUsersDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Invite users to an organization","tags":["organizations"]}},"/organization/{orgId}/invitation/{invitationId}":{"delete":{"description":"Deletes a pending invitation of the organization. Requires a member role or above in the organization and an active plan (403 otherwise). Returns 404 for an unknown invitation and 400 when it was already accepted or rejected.","operationId":"OrganizationController_removeInvitation","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"invitationId","required":true,"in":"path","description":"Id of the existing invitation","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a pending invitation","tags":["organizations"]}},"/organization/{orgId}/invitation/{invitationId}/resend":{"post":{"description":"Issues a new invitation token, valid for one month, and sends the invitation email again; a rejected invitation goes back to pending. Requires a member role or above in the organization and an active plan (403 otherwise). Returns 404 for an unknown invitation and 400 when it was already accepted.","operationId":"OrganizationController_resendInvitation","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"invitationId","required":true,"in":"path","description":"Id of the existing invitation","schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Resend an invitation","tags":["organizations"]}},"/organization/{orgId}/invitation/accept":{"post":{"description":"Accepts an invitation using the emailed token and email and returns the organization details. When the invitee has no account yet, `name` and `password` are required (400 otherwise), the account is created and signed in, and `login` holds its tokens and profile; for an existing user `login` is null. No auth, but the organization needs an active plan (403). Returns 404 for an unknown organization or invitation, and 400 when the token has expired or the invitation is no longer pending.","operationId":"OrganizationController_acceptInvitation","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Accept invitation body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptOrganizationInvitationRequestDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/InvitationAcceptResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Accept an organization invitation","tags":["organizations"]}},"/organization/{orgId}/invitation/reject":{"post":{"description":"Rejects an invitation using the emailed token and email, with an optional reason, and notifies the organization. No auth. Returns 404 for an unknown organization or invitation, and 400 when the token has expired or the invitation is no longer pending. The response has no `data`.","operationId":"OrganizationController_rejectInvitation","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Reject invitation body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectOrganizationInvitationRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["statusCode","timestamp"],"properties":{"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Reject an organization invitation (internal)","tags":["organizations"],"x-internal":true}},"/organization/{orgId}/update-user-role":{"patch":{"description":"Sets the organization role of each listed user. Requires an org admin or owner and an active plan (403 otherwise). Users who are not members are skipped without an error.","operationId":"OrganizationController_updateAccess","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationUserAccessDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Change members' organization roles (internal)","tags":["organizations"],"x-internal":true}},"/organization/{orgId}/user":{"patch":{"description":"Updates each listed member's organization role and/or their name and login email. Name and email change on the user account itself, not only in this organization, so they can be changed only for a member who belongs to no other organization (403 otherwise, and nothing in the request is applied). Requires an org admin or owner and an active plan (403 otherwise). Returns 400 when an entry has none of role, name or email or the new email belongs to another account, and 404 when a user is not a member.","operationId":"OrganizationController_updateOrganizationUsers","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationUsersDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update organization members","tags":["organizations"]}},"/organization/{orgId}/user/{userId}":{"delete":{"description":"Removes the user from the organization, deletes their invitations and case access in it, and frees one seat on the plan. Requires an org admin or owner and an active plan (403 otherwise).","operationId":"OrganizationController_removeUserFromOrg","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","description":"Id of the user to remove","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove a member from an organization","tags":["organizations"]}},"/organization/{orgId}/contact-admin":{"post":{"description":"Queues an email to the organization asking for more devices on behalf of the caller, with the given text or a default message, optionally for a case. Requires a role in the organization and an active plan (403 otherwise). Returns 404 for an unknown organization.","operationId":"OrganizationController_contactAdminForDevice","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceRequestContactAdminDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Ask the organization for more devices (internal)","tags":["organizations"],"x-internal":true}},"/organization/tags/{orgId}":{"get":{"description":"Lists the organization's tags, filterable by type, name, severity and origin; paginated, 10 per page by default. Requires a member role or above in the organization and an active plan (403 otherwise).","operationId":"OrganizationTagsController_search","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"name","required":false,"in":"query","schema":{"type":"string"}},{"name":"severity","required":false,"in":"query","schema":{"default":"info","type":"string","enum":["info","warning","danger"]}},{"name":"type","required":false,"in":"query","schema":{"default":"organization","type":"string","enum":["system","organization","case","user"]}},{"name":"origin","required":false,"in":"query","description":"Filter by how the tag was created. Use \"manual\" to hide auto-import tags from the picker.","schema":{"type":"string","enum":["manual","auto-import"]}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"name","type":"string","enum":["createdAt","name"]}},{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TagEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List organization tags","tags":["organizations"]},"post":{"description":"Creates an organization tag (severity `info` and the default colors unless given) and returns it. Requires a member role or above in the organization and an active plan (403 otherwise). Returns 400 when the organization already has a tag with that name (case-insensitive).","operationId":"OrganizationTagsController_create","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationTagDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/TagEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create an organization tag","tags":["organizations"]}},"/organization/tags/{orgId}/{tagId}":{"patch":{"description":"Changes an organization tag's name, colors or severity and returns it. Requires a member role or above in the organization and an active plan (403 otherwise). Returns 404 for an unknown tag, and 400 when the new name is taken or nothing was sent.","operationId":"OrganizationTagsController_update","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"tagId","required":true,"in":"path","description":"Id of the tag","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationTagDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/TagEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update an organization tag","tags":["organizations"]},"delete":{"description":"Deletes an organization tag and detaches it from everything it was attached to. Requires an org admin or owner and an active plan (403 otherwise). Returns 404 for an unknown tag.","operationId":"OrganizationTagsController_delete","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"tagId","required":true,"in":"path","description":"Id of the tag","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete an organization tag","tags":["organizations"]}},"/case":{"get":{"description":"Lists the cases the caller can see in the organization from the `organization` header, most recently updated first by default, including cases shared with the caller by another firm. Each row carries the case plus the counts the case list shows (extraction codes, data used, data-source statuses, conversations). Use GET /case/:caseId for a single case.","operationId":"CaseController_search","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"organization","required":true,"in":"header","description":"id of the selected (active) organization","schema":{"type":"string"}},{"name":"hasUpdates","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"skipPagination","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["open","closed","archived","pending"]}},{"name":"priority","required":false,"in":"query","schema":{"type":"string","enum":["low","medium","high","urgent"]}},{"name":"assignStatus","required":false,"in":"query","schema":{"type":"string","enum":["assigned","unassigned"]}},{"name":"assignedTo","required":false,"in":"query","schema":{"type":"string"}},{"name":"users","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"isDefault","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"updatedAt","type":"string","enum":["createdAt","updatedAt","name"]}},{"name":"isArchived","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseListItemEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List cases","tags":["cases"]},"post":{"description":"Creates a case in the organization from the `organization` header and returns it with its members and pending invitations. The caller becomes the case owner.","operationId":"CaseController_create","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCaseDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseViewEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a case","tags":["cases"]}},"/case/check-name":{"get":{"description":"Returns whether a case with this name (case-insensitive) already exists in the organization. Call it before POST /case to show a name clash early.","operationId":"CaseController_checkCaseName","parameters":[{"name":"name","required":true,"in":"query","schema":{"minLength":3,"maxLength":256,"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CheckCaseNameResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Check whether a case name is free","tags":["cases"]}},"/case/{caseId}":{"get":{"description":"Returns one case with its members, pending invitations, active extraction codes and conversation count. For an opposing-counsel caller, members and counts are limited to what that side may see.","operationId":"CaseController_getCaseDetails","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseViewEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a case","tags":["cases"]},"patch":{"description":"Updates the case name, description, matter id, status, priority, metadata or tags, and returns the updated case. Renaming also changes the slug.","operationId":"CaseController_update","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseViewEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update a case","tags":["cases"]}},"/case/{caseId}/data-sources":{"get":{"description":"Lists the data sources enabled on the case (for example iMessage or Gmail), as ids of the master data sources.","operationId":"CaseController_fetchCaseDataSources","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseDataSourceListItemEntity"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a case's data sources","tags":["cases"]}},"/case/slug/{slug}":{"get":{"description":"Same as GET /case/:caseId, looked up by the case slug within the organization from the `organization` header. Also finds a case another firm shared with the caller.","operationId":"CaseController_getCaseDetailsBySlug","parameters":[{"name":"slug","required":true,"in":"path","description":"slug of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseViewEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a case by slug","tags":["cases"]}},"/case/{caseId}/archive":{"post":{"description":"Archives the case. Archived cases stay readable but are hidden from the default case list.","operationId":"CaseController_archiveCase","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArchiveCaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Archive a case","tags":["cases"]}},"/case/{caseId}/unarchive":{"post":{"description":"Restores an archived case to the active case list.","operationId":"CaseController_unarchiveCase","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Unarchive a case","tags":["cases"]}},"/case/{caseId}/conversations":{"delete":{"description":"Removes every conversation from the case. Conversations linked only to this case are deleted; conversations also linked to other cases are only unlinked from this one. This cannot be undone.","operationId":"CaseController_deleteCaseConversations","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove all conversations from a case","tags":["cases"]}},"/case/{caseId}/link-conversations":{"patch":{"description":"Links the given device conversations to the case and returns the updated case.","operationId":"CaseController_linkConversations","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkConversationsToCaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseViewEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Link conversations to a case","tags":["cases"]}},"/case/{caseId}/unlink-conversations":{"patch":{"description":"Removes the given device conversations from the case and returns the updated case.","operationId":"CaseController_unlinkConversations","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlinkConversationsFromCaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseViewEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Unlink conversations from a case","tags":["cases"]}},"/case/{caseId}/export":{"post":{"description":"Queues an export of the case's conversations (PDF, RSMF and other formats). The export runs in the background; poll GET /case/:caseId/export or listen on the case socket for progress, then download the export's file.","operationId":"CaseController_export","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseCollectionExportDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionExportEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a case export","tags":["cases"]},"get":{"description":"Lists the case's export jobs with their status, progress and file.","operationId":"CaseController_searchExports","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"source","required":false,"in":"query","schema":{"type":"string","enum":["conversation","browser-history","emails","case-files","case-attachments","financial","linkedin","ai-chat","contacts"]}},{"name":"type","required":false,"in":"query","schema":{"type":"string","enum":["doc","text","csv","pdf","goodfact","rsmf","eml","files-zip"]}},{"name":"exportId","required":false,"in":"query","schema":{"type":"string"}},{"name":"exportedBy","required":false,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"createdAt","type":"string","enum":["createdAt","exportedAt"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseCollectionExportEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List case exports","tags":["cases"]}},"/case/{caseId}/export/{exportId}":{"delete":{"description":"Deletes the export job and all of its chunk records.","operationId":"CaseController_deleteExport","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"exportId","required":true,"in":"path","description":"Id of the export","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete an export","tags":["cases"]}},"/case/{caseId}/export/{exportId}/rerun":{"post":{"description":"Queues an export that did not finish successfully again, with the same settings. An export that already completed is rejected.","operationId":"CaseController_rerunExport","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"exportId","required":true,"in":"path","description":"Id of the export","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Re-run an export","tags":["cases"]}},"/case/{caseId}/extraction":{"get":{"description":"Lists the case's extraction codes (the codes custodians use to share data from their devices) with their upload status and, on cases shared with opposing counsel, their review status.","operationId":"CaseController_searchExtractionCode","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"skipPagination","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"extractionCode","required":false,"in":"query","schema":{"maxLength":100,"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"updatedAt","type":"string","enum":["createdAt","updatedAt"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a case's extraction codes","tags":["cases"]},"post":{"description":"Creates an extraction code for a custodian. The code is emailed only when sendEmail is true and custodianEmail is set, and, on a case shared with opposing counsel, only once the code is approved. The custodian enters the code in the Hearsay app to share data into this case.","operationId":"CaseController_createExtractionCode","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExtractionCodeDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create an extraction code","tags":["cases"]}},"/case/{caseId}/data-status":{"get":{"description":"Per data source, the status of each source the custodians shared, with status percentages. Superseded by data-status-tree-v3, which most clients should use.","operationId":"CaseController_fetchCaseDataStatus","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Tag ids (comma-separated). Filters device conversations, emails, files, and financial sources. Excludes discord/reddit.","schema":{"type":"array","items":{"type":"string"}}},{"name":"searchMatchType","required":false,"in":"query","description":"Match type for message text search. FUZZY allows typo tolerance, EXACT requires exact token match.","schema":{"default":"FUZZY","type":"string","enum":["FUZZY","EXACT"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseDataStatusResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get data-collection status (v1)","tags":["cases"]}},"/case/{caseId}/data-status-v2":{"get":{"description":"Data-collection status grouped by data source, with item counts and the extraction code each source came from. For the full tree view, use data-status-tree-v3.","operationId":"CaseController_fetchCaseDataStatusV2","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Tag ids (comma-separated). Filters device conversations, emails, files, and financial sources. Excludes discord/reddit.","schema":{"type":"array","items":{"type":"string"}}},{"name":"searchMatchType","required":false,"in":"query","description":"Match type for message text search. FUZZY allows typo tolerance, EXACT requires exact token match.","schema":{"default":"FUZZY","type":"string","enum":["FUZZY","EXACT"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseDataStatusV2ResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get data-collection status (v2)","tags":["cases"]}},"/case/{caseId}/data-status-tree":{"get":{"description":"Data-collection status as a tree of custodians, devices and sources. Kept as a fallback for data-status-tree-v3.","operationId":"CaseController_fetchCaseDataStatusTree","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Tag ids (comma-separated). Filters device conversations, emails, files, and financial sources. Excludes discord/reddit.","schema":{"type":"array","items":{"type":"string"}}},{"name":"searchMatchType","required":false,"in":"query","description":"Match type for message text search. FUZZY allows typo tolerance, EXACT requires exact token match.","schema":{"default":"FUZZY","type":"string","enum":["FUZZY","EXACT"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseDataStatusListTreeResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get data-collection status as a tree","tags":["cases"]}},"/case/{caseId}/data-status-tree-v3":{"get":{"description":"The current data-collection view: a tree of extraction codes, devices, email accounts, files and financial accounts with item counts and, on opposing-counsel cases, items pending review.","operationId":"CaseController_fetchCaseDataStatusTreeV3","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Tag ids (comma-separated). Filters device conversations, emails, files, and financial sources. Excludes discord/reddit.","schema":{"type":"array","items":{"type":"string"}}},{"name":"searchMatchType","required":false,"in":"query","description":"Match type for message text search. FUZZY allows typo tolerance, EXACT requires exact token match.","schema":{"default":"FUZZY","type":"string","enum":["FUZZY","EXACT"]}},{"name":"conversationLimit","required":false,"in":"query","description":"Max conversations per source-type folder","schema":{"default":25,"type":"number"}},{"name":"fileLimit","required":false,"in":"query","description":"Max files per file-provider folder","schema":{"default":25,"type":"number"}},{"name":"sortConversationsBy","required":false,"in":"query","description":"Sort field for conversation children","schema":{"default":"lastMessageDate","type":"string","enum":["lastMessageDate","displayName"]}},{"name":"sortConversationsOrder","required":false,"in":"query","description":"Sort direction override. Defaults to DESC for lastMessageDate, ASC for displayName.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseDataStatusTreeV3ResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get data-collection status (tree v3)","tags":["cases"]}},"/case/{caseId}/extraction/{extractionCode}":{"patch":{"description":"Updates an extraction code's label, due date, configuration or custodian. Configuration cannot change once the code is locked.","operationId":"CaseController_updateExtractionCode","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"extractionCode","required":true,"in":"path","description":"extraction code","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateExtractionCodeDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update an extraction code","tags":["cases"]},"delete":{"description":"Deletes an extraction code that has not collected any data yet. Returns 409 once data has been collected with it, and 403 while code deletion is turned off.","operationId":"CaseController_deleteExtractionCode","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"extractionCode","required":true,"in":"path","description":"extraction code","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete an extraction code","tags":["cases"]}},"/case/{caseId}/extraction/{extractionCode}/custodian":{"patch":{"description":"Updates the custodian's name, email or phone on an extraction code and, when the email changes and the code is usable, re-sends the code email.","operationId":"CaseController_updateCustodianContact","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"extractionCode","required":true,"in":"path","description":"extraction code","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustodianContactDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update a custodian's contact details","tags":["cases"]}},"/case/{caseId}/extraction/{extractionCode}/send-email":{"post":{"description":"Queues the extraction-code email to the code's custodian.","operationId":"CaseController_sendExtractionCodeEmail","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"extractionCode","required":true,"in":"path","description":"extraction code","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendExtractionCodeEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Email an extraction code","tags":["cases"]}},"/case/{caseId}/search-tracking":{"get":{"description":"Lists the conversation searches custodians ran in the Hearsay app for this case, with the search filters they used.","operationId":"CaseController_fetchDeviceSearchTrackingData","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"conversationId","required":false,"in":"query","schema":{"type":"string"}},{"name":"deviceId","required":false,"in":"query","schema":{"type":"string"}},{"name":"extractionCodeId","required":false,"in":"query","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceSearchTrackingEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List conversation searches","tags":["cases"]}},"/case/{caseId}/activity-logs":{"get":{"description":"Lists device activity log entries for the case (backups, uploads and similar events).","operationId":"CaseController_fetchDeviceActivityLogsData","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"deviceId","required":false,"in":"query","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceActivityLogSingleEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List device activity","tags":["cases"]}},"/case/{caseId}/download-attachments":{"post":{"description":"Queues a zip of the attachments in the given conversations. The job runs in the background; listen on the case socket or poll GET /case/:caseId/export for the file.","operationId":"CaseController_downloadConversationAttachments","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadConversationAttachmentsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionExportEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Request a zip of conversation attachments","tags":["cases"]}},"/case/access/invitations":{"get":{"description":"Searches case invitations in organizations where the caller is an owner or admin, optionally narrowed to one organization, case, status or access level. For one case's invitations, use GET /case/access/:orgId/case/:caseId/invitations.","operationId":"CaseAccessController_searchInvitations","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"organization","required":false,"in":"query","schema":{"type":"string"}},{"name":"case","required":false,"in":"query","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["pending","accepted","rejected"]}},{"name":"accessLevel","required":false,"in":"query","schema":{"type":"string","enum":["owner","admin","commenter","viewer"]}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"updatedAt","type":"string","enum":["createdAt","updatedAt","email"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseInvitationListItemEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search case invitations across organizations","tags":["case-access"]}},"/case/access/pending-invitations":{"get":{"description":"Lists the pending case invitations addressed to the caller, optionally narrowed to one organization or access level.","operationId":"CaseAccessController_searchPendingInvitations","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"organization","required":false,"in":"query","schema":{"type":"string"}},{"name":"accessLevel","required":false,"in":"query","schema":{"type":"string","enum":["owner","admin","commenter","viewer"]}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"updatedAt","type":"string","enum":["createdAt","updatedAt","email"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseInvitationListItemEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List my pending case invitations","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/members":{"get":{"description":"Lists the active members of a case with their access level and type. On a case shared with opposing counsel, each side sees its own members plus the other side's lead contact.","operationId":"CaseAccessController_listCaseMembers","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"level","required":false,"in":"query","schema":{"type":"string","enum":["owner","admin","commenter","viewer"]}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"joinedAt","type":"string","enum":["name","joinedAt"]}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"orgId","required":true,"in":"path","description":"Id of the org","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseMemberEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List case members","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/invitations":{"get":{"description":"Lists one case's invitations, optionally filtered by status or by invitee name or email. On a case shared with opposing counsel, each side sees only the invitations it may see.","operationId":"CaseAccessController_listCaseInvitations","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["pending","accepted","rejected"]}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"date","type":"string","enum":["name","date"]}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"orgId","required":true,"in":"path","description":"Id of the org","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseInvitationListItemEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List case invitations","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/add-all-org-users":{"post":{"description":"Gives every member of the organization access to the case at the requested access level.","operationId":"CaseAccessController_addAllOrgUsersToCase","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Add all users request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAllOrgUsersToCaseDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Add every organization member to a case","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/invite":{"post":{"description":"Invites one or more people to the case by email. Each invitee receives an email with a link to accept.","operationId":"CaseAccessController_inviteUsers","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Invitation request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteCaseUsersDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Invite people to a case","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/invitation/{invitationId}":{"delete":{"description":"Deletes a pending case invitation. Accepted or rejected invitations cannot be cancelled.","operationId":"CaseAccessController_removeInvitation","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"invitationId","required":true,"in":"path","description":"Id of the invitation to remove","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Cancel a pending invitation","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/invitation/{invitationId}/resend":{"post":{"description":"Issues a new invitation link (valid for one month) and emails it to the invitee again.","operationId":"CaseAccessController_resendInvitation","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"invitationId","required":true,"in":"path","description":"Id of the existing invitation","schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Resend an invitation","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/invitation/accept":{"post":{"description":"Accepts an invitation using the token from the invite email. Public: no sign-in needed. A new invitee must send name and password; their account is created and `login` carries their session.","operationId":"CaseAccessController_acceptInvitation","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Accept invitation body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptCaseInvitationDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseInvitationAcceptResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Accept a case invitation","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/invitation/reject":{"post":{"description":"Rejects an invitation using the token from the invite email, with an optional reason. Public: no sign-in needed. Returns no data.","operationId":"CaseAccessController_rejectInvitation","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Reject invitation body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectCaseInvitationRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["statusCode","timestamp"],"properties":{"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Reject a case invitation","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/update-user-access":{"patch":{"description":"Changes the access level of one or more members of the case.","operationId":"CaseAccessController_updateAccess","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseUserAccessDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Change a member's case access","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/user/{userId}":{"delete":{"description":"Removes a member's access to the case.","operationId":"CaseAccessController_removeUserFromCase","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","description":"Id of the user to remove","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove a member from a case","tags":["case-access"]}},"/case/tags/{caseId}":{"get":{"description":"Lists the tags defined for the case.","operationId":"CaseTagsController_search","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"name","required":false,"in":"query","schema":{"type":"string"}},{"name":"severity","required":false,"in":"query","schema":{"default":"info","type":"string","enum":["info","warning","danger"]}},{"name":"type","required":false,"in":"query","schema":{"default":"organization","type":"string","enum":["system","organization","case","user"]}},{"name":"origin","required":false,"in":"query","description":"Filter by how the tag was created. Use \"manual\" to hide auto-import tags from the picker.","schema":{"type":"string","enum":["manual","auto-import"]}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"name","type":"string","enum":["createdAt","name"]}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TagEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a case's tags","tags":["cases"]},"post":{"description":"Creates a tag in the case.","operationId":"CaseTagsController_create","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCaseTagDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/TagEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a tag","tags":["cases"]}},"/case/tags/{caseId}/{tagId}":{"patch":{"description":"Changes a tag's name, colors or severity.","operationId":"CaseTagsController_update","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"tagId","required":true,"in":"path","description":"Id of the tag","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseTagDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/TagEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update a tag","tags":["cases"]},"delete":{"description":"Deletes a tag from the case.","operationId":"CaseTagsController_delete","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"tagId","required":true,"in":"path","description":"Id of the tag","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a tag","tags":["cases"]}},"/case/collections/{caseId}":{"get":{"description":"Lists the case's message collections (curated sets of messages), optionally filtered by name or archived state.","operationId":"CaseCollectionController_search","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"name","required":false,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"updatedAt","type":"string","enum":["createdAt","updatedAt","name"]}},{"name":"isArchived","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseCollectionEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a case's collections","tags":["collections"]},"post":{"description":"Creates an empty collection in the case.","operationId":"CaseCollectionController_create","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCaseCollectionDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a collection","tags":["collections"]}},"/case/collections/{caseId}/bulk":{"post":{"description":"Searches the case's messages for the given terms and creates one collection per matching conversation, holding the matching messages. Returns how many collections were created.","operationId":"CaseCollectionController_createBulkCollections","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBulkCollectionsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BulkCollectionCreateResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create collections from search terms","tags":["collections"]}},"/case/collections/{caseId}/{collectionId}/entries":{"get":{"description":"Lists the entries of a collection in order, each with its device message, optionally filtered by text or tags.","operationId":"CaseCollectionController_searchEntries","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"linkedContacts","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"devices","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"startDate","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"endDate","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"source","required":false,"in":"query","schema":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"]}},{"name":"type","required":false,"in":"query","schema":{"type":"string","enum":["message","text","line_break","custom"]}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseCollectionResponseEntryEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a collection's entries","tags":["collections"]}},"/case/collections/{caseId}/{collectionId}":{"patch":{"description":"Changes a collection's name (and its slug).","operationId":"CaseCollectionController_update","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseCollectionDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Rename a collection","tags":["collections"]},"delete":{"description":"Deletes a collection and its entries. The messages themselves stay in the case.","operationId":"CaseCollectionController_delete","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a collection","tags":["collections"]},"get":{"description":"Returns one collection.","operationId":"CaseCollectionController_getById","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a collection","tags":["collections"]}},"/case/collections/{caseId}/slug/{slug}":{"get":{"description":"Returns one collection, looked up by its slug within the case.","operationId":"CaseCollectionController_getBySlug","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"slug","required":true,"in":"path","description":"slug of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a collection by slug","tags":["collections"]}},"/case/collections/{caseId}/{collectionId}/add":{"patch":{"description":"Adds the given device messages to the collection.","operationId":"CaseCollectionController_addEntries","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddEntriesToCaseCollectionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Add messages to a collection","tags":["collections"]}},"/case/collections/{caseId}/{collectionId}/add-all":{"patch":{"description":"Adds every message of the given conversations (up to 100) to the collection.","operationId":"CaseCollectionController_addAllMessages","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAllMessagesToCollectionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Add whole conversations to a collection","tags":["collections"]}},"/case/collections/{caseId}/{collectionId}/remove":{"patch":{"description":"Removes the given messages from the collection.","operationId":"CaseCollectionController_removeEntries","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveEntriesFromCaseCollectionDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove messages from a collection","tags":["collections"]}},"/case/collections/{caseId}/{collectionId}/remove/all":{"patch":{"description":"Removes every entry from the collection. The collection itself stays.","operationId":"CaseCollectionController_removeAllMessages","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Empty a collection","tags":["collections"]}},"/case/collections/{caseId}/{collectionId}/insert-entry":{"post":{"description":"Inserts a message or a custom entry at the given position in the collection, shifting later entries down.","operationId":"CaseCollectionController_insertAtPosition","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsertEntryAtPositionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntryEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Insert an entry at a position","tags":["collections"]}},"/case/collections/{caseId}/{collectionId}/entry/{entryId}/{position}":{"patch":{"description":"Moves an entry to a new position in the collection.","operationId":"CaseCollectionController_moveEntry","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"collectionId","required":true,"in":"path","description":"Id of the collection","schema":{"type":"string"}},{"name":"entryId","required":true,"in":"path","description":"Id of the entry","schema":{"type":"string"}},{"name":"position","required":true,"in":"path","description":"position to move the entry","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionEntryEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Move an entry","tags":["collections"]}},"/case/contacts/{orgId}/case/{caseId}":{"get":{"description":"Lists the contacts found on the case's devices, optionally filtered by device or conversation, or searched by text.","operationId":"CaseContactController_search","parameters":[{"name":"orgId","required":true,"in":"path","description":"ID of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"ID of the case","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"conversation","required":false,"in":"query","schema":{"type":"string"}},{"name":"device","required":false,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"name","type":"string","enum":["createdAt","updatedAt","name"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceContactEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a case's contacts","tags":["case-contact"]}},"/case/contacts/{orgId}/case/{caseId}/{contactId}":{"patch":{"description":"Edits a contact's name, email or phone. The values the device reported are kept, so the contact shows as edited.","operationId":"CaseContactController_update","parameters":[{"name":"orgId","required":true,"in":"path","description":"ID of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"ID of the case","schema":{"type":"string"}},{"name":"contactId","required":true,"in":"path","description":"ID of the contact","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseContactDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceContactEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Edit a contact","tags":["case-contact"]}},"/case/contacts/{orgId}/case/{caseId}/{sourceContactId}/{targetContactId}":{"patch":{"description":"Links the source contact to the target contact so they are treated as the same person.","operationId":"CaseContactController_link","parameters":[{"name":"orgId","required":true,"in":"path","description":"ID of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"ID of the case","schema":{"type":"string"}},{"name":"sourceContactId","required":true,"in":"path","description":"ID of the source contact","schema":{"type":"string"}},{"name":"targetContactId","required":true,"in":"path","description":"ID of the target contact","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceContactEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Link two contacts","tags":["case-contact"]}},"/case/contacts/{orgId}/case/{caseId}/export":{"post":{"description":"Queues a CSV export of the case's contacts and returns the export job id.","operationId":"CaseContactController_exportContacts","parameters":[{"name":"orgId","required":true,"in":"path","description":"ID of the organization","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"ID of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportCaseContactsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"jobId":{"type":"string","example":"job-1234"}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export contacts to CSV","tags":["case-contact"]}},"/case/conversations/{caseId}/archive":{"post":{"description":"Archives the given conversations in the case so they are hidden from the default conversation list.","operationId":"CaseConversationController_archiveConversations","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArchiveConversationsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Archive conversations","tags":["case-contact"]}},"/case/conversations/{caseId}/unarchive":{"post":{"description":"Restores archived conversations to the case's conversation list.","operationId":"CaseConversationController_unarchiveConversations","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnarchiveConversationsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Unarchive conversations","tags":["case-contact"]}},"/case/conversations/{caseId}/{conversationId}":{"patch":{"description":"Updates a conversation's display name or other editable details and returns the conversation.","operationId":"CaseConversationController_editConversation","parameters":[{"name":"caseId","required":true,"in":"path","description":"id of the case","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditConversationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceConversationEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Edit a conversation","tags":["case-contact"]}},"/case/conversations/{caseId}/{conversationId}/tags":{"patch":{"description":"Replaces the tags applied to a conversation and returns the conversation.","operationId":"CaseConversationController_updateConversationTags","parameters":[{"name":"caseId","required":true,"in":"path","description":"id of the case","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"id of the conversation","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseFileTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceConversationEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Set a conversation's tags","tags":["case-contact"]}},"/case/conversations/{caseId}/add":{"post":{"description":"Creates a conversation on a device in the case from the given participant and source details.","operationId":"CaseConversationController_addConversation","parameters":[{"name":"caseId","required":true,"in":"path","description":"id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddConversationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceConversationEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Add a conversation manually","tags":["case-contact"]}},"/case/browser-history/{caseId}/history":{"get":{"description":"Lists the browser history custodians shared into the case, optionally filtered by device, sorted by last visit by default.","operationId":"CaseBrowserHistoryController_fetchCaseDataStatus","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"skipPagination","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"lastDate","type":"string","enum":["firstDate","lastDate"]}},{"name":"devices","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BrowserHistoryEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List browser history","tags":["case-browser-history"]}},"/cases/files/{caseId}":{"get":{"description":"Lists the files uploaded to the case, optionally filtered by extraction code, opposing-counsel shared status, tags or text. On a case shared with opposing counsel, files are limited to what the caller may see.","operationId":"CaseFilesController_search","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"category","required":false,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"createdAt","type":"string","enum":["createdAt","name","type","size"]}},{"name":"sharedStatus","required":false,"in":"query","description":"Opposing-council only: filter to files whose ocSharedStatus matches (e.g. `pending` for \"show me what I have not shared yet\"). Ignored for non-OC callers.","schema":{"type":"string","enum":["shared","pending","not_gated"]}},{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseFileEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List case files","tags":["cases/files"]}},"/cases/files/{caseId}/add-files":{"post":{"description":"Adds already-uploaded files to the case, optionally with a category.","operationId":"CaseFilesController_addCaseFiles","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddCaseFilesViaCaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Add files to a case","tags":["cases/files"]}},"/cases/files/{caseId}/zip-download":{"post":{"description":"Queues a zip of the given case files. The job runs in the background; poll GET /case/:caseId/export or listen on the case socket for the file.","operationId":"CaseFilesController_requestZipDownload","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestCaseFilesZipDownloadDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseCollectionExportEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Request a zip of case files","tags":["cases/files"]}},"/cases/files/{caseFileId}/category":{"patch":{"description":"Sets or clears the category of a case file and returns the file.","operationId":"CaseFilesController_updateCaseFileCategory","parameters":[{"name":"caseFileId","required":true,"in":"path","description":"Id of the case file","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseFileCategoryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseFileEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Set a file's category","tags":["cases/files"]}},"/cases/files/{caseFileId}/tags":{"patch":{"description":"Replaces the tags applied to a case file and returns the file.","operationId":"CaseFilesController_updateCaseFileTags","parameters":[{"name":"caseFileId","required":true,"in":"path","description":"Id of the case file","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseFileTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseFileEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Set a file's tags","tags":["cases/files"]}},"/case/media/{caseId}":{"get":{"description":"Lists the photos and videos shared into the case, optionally filtered by device or searched by name.","operationId":"CaseMediaController_searchCaseMedia","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"device","required":false,"in":"query","description":"Filter by device MongoDB id","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"createdAt","type":"string","enum":["createdAt","fileName","externalId"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceMediaEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List case media","tags":["cases/media"]}},"/case/notes/{caseId}":{"get":{"description":"Lists the device notes shared into the case, optionally filtered by device or searched by text.","operationId":"CaseNotesController_searchCaseNotes","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"device","required":false,"in":"query","description":"Filter by device MongoDB id","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"createdAt","type":"string","enum":["createdAt","title","externalId"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceNotesEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List case notes","tags":["cases/notes"]}},"/case/{caseId}/suggest/{codeId}":{"post":{"description":"On a case shared with opposing counsel, proposes a configuration for an extraction code. If the caller already has an open suggestion for the code, it is updated instead.","operationId":"CaseExtractionCodeController_createOrUpdateOwnOpen","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExtractionCodeSuggestionDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeSuggestionEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Suggest an extraction-code configuration","tags":["cases/extraction-code/suggestions"]},"get":{"description":"Lists the configuration suggestions made for an extraction code, newest first by default, optionally filtered by status.","operationId":"CaseExtractionCodeController_list","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"status","required":false,"in":"query","description":"Filter suggestions by status","schema":{"type":"string","enum":["open","approved","rejected","closed"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeSuggestionEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List suggestions for an extraction code","tags":["cases/extraction-code/suggestions"]}},"/case/{caseId}/suggest/{codeId}/{suggestionId}":{"patch":{"description":"Changes the proposed configuration of the caller's own open suggestion.","operationId":"CaseExtractionCodeController_updateOwn","parameters":[{"name":"suggestionId","required":true,"in":"path","description":"Id of the suggestion","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{}},{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateExtractionCodeSuggestionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeSuggestionEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Edit my open suggestion","tags":["cases/extraction-code/suggestions"]},"delete":{"description":"Deletes the caller's own open suggestion. Returns no body.","operationId":"CaseExtractionCodeController_deleteOwn","parameters":[{"name":"suggestionId","required":true,"in":"path","description":"Id of the suggestion","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{}},{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{}}],"responses":{"204":{"description":"Suggestion withdrawn"}},"security":[{"access-token":[]}],"summary":"Withdraw my open suggestion","tags":["cases/extraction-code/suggestions"]}},"/case/{caseId}/suggest/{codeId}/{suggestionId}/message":{"post":{"description":"Adds a message to an open suggestion's discussion thread.","operationId":"CaseExtractionCodeController_appendMessage","parameters":[{"name":"suggestionId","required":true,"in":"path","description":"Id of the suggestion","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{}},{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExtractionCodeSuggestionMessageDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeSuggestionEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Comment on a suggestion","tags":["cases/extraction-code/suggestions"]}},"/case/{caseId}/suggest/{codeId}/{suggestionId}/approve":{"patch":{"description":"Applies the suggested configuration to the extraction code, approves and locks the code, and closes other open suggestions. Only the side opposite the suggester can approve.","operationId":"CaseExtractionCodeController_approve","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"suggestionId","required":true,"in":"path","description":"Id of the suggestion","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}},{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeSuggestionEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Approve a suggestion","tags":["cases/extraction-code/suggestions"]}},"/case/{caseId}/suggest/{codeId}/{suggestionId}/reject":{"patch":{"description":"Rejects a suggestion, with an optional message. Only the side opposite the suggester can reject. The extraction code is not changed.","operationId":"CaseExtractionCodeController_reject","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"suggestionId","required":true,"in":"path","description":"Id of the suggestion","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}},{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectExtractionCodeSuggestionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeSuggestionEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Reject a suggestion","tags":["cases/extraction-code/suggestions"]}},"/case/{caseId}/extraction-code/{codeId}/approve":{"patch":{"description":"Opposing counsel approves an extraction code awaiting review, with an optional message. Approval unblocks the custodian email and locks the code's configuration.","operationId":"CaseExtractionCodeReviewController_approve","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewExtractionCodeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Approve an extraction code","tags":["cases/extraction-code/review"]}},"/case/{caseId}/extraction-code/{codeId}/reject":{"patch":{"description":"Opposing counsel rejects an extraction code awaiting review, with an optional message.","operationId":"CaseExtractionCodeReviewController_reject","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewExtractionCodeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Reject an extraction code","tags":["cases/extraction-code/review"]}},"/case/{caseId}/oc-approvals/approve":{"post":{"description":"Opposing counsel approves a data provider's data (all of it, or a subset of items) so the case owner can see it. Approvals add to earlier ones.","operationId":"OcApprovalsController_approve","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OcDataApprovalRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OcDataApprovalEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Approve data for the other side","tags":["cases/oc-approvals"]}},"/case/{caseId}/oc-approvals/reject":{"post":{"description":"Removes an approval for a data provider, or only for the given items. Returns no body.","operationId":"OcApprovalsController_reject","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OcDataApprovalRequestDto"}}}},"responses":{"204":{"description":"Approval removed"}},"security":[{"access-token":[]}],"summary":"Withdraw data approval","tags":["cases/oc-approvals"]}},"/case/{caseId}/oc-approvals":{"get":{"description":"Lists the case's data approvals, optionally for one data-provider type.","operationId":"OcApprovalsController_list","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"dataProviderType","required":false,"in":"query","description":"Filter approval rows by data provider type","schema":{"type":"string","enum":["Device","UserEmailDataProvider","CaseFileProvider","FinancialAccountProvider","LegalDocumentProvider","DiscordUserProvider","RedditUserProvider","LinkedInUserProvider","AiChatProvider"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OcDataApprovalEntity"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List data approvals","tags":["cases/oc-approvals"]}},"/case/access/{orgId}/case/{caseId}/oc-members/invite":{"post":{"description":"Opposing counsel invites a colleague from their own firm to the shared case.","operationId":"OcMembersController_inviteOcColleague","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"orgId","required":true,"in":"path","description":"Id of the org","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"OC colleague invitation body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteOcColleagueDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Invite a colleague (opposing counsel)","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/oc-members/{userId}":{"delete":{"description":"Opposing counsel removes a colleague they added from the shared case.","operationId":"OcMembersController_removeOcColleague","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","description":"Id of the OC member to remove","schema":{"type":"string"}},{"name":"orgId","required":true,"in":"path","description":"Id of the org","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove a colleague (opposing counsel)","tags":["case-access"]}},"/case/access/{orgId}/case/{caseId}/oc-members/invitation/{invitationId}":{"delete":{"description":"Opposing counsel cancels a pending invitation they sent to a colleague.","operationId":"OcMembersController_cancelOwnOcInvitation","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"invitationId","required":true,"in":"path","description":"Id of the pending OC invitation to cancel","schema":{"type":"string"}},{"name":"orgId","required":true,"in":"path","description":"Id of the org","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Cancel a colleague invitation (opposing counsel)","tags":["case-access"]}},"/extraction/collection-events":{"post":{"description":"Desktop and portal plumbing: records a batch of up to 50 collection events against the caller's extraction code and returns a result per event (accepted, duplicate, rejected or retry). Returns 404 when collection events are turned off, 411 without a Content-Length header and 413 over 256 KB.","operationId":"CollectionEventsIngestionController_ingest","parameters":[{"name":"content-length","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestCollectionEventsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/IngestCollectionEventsResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Record collection events (internal)","tags":["extraction"],"x-internal":true}},"/admin/collection-ops/queue":{"get":{"description":"Lists collections, one row per extraction code, for a filter (`all`, `priority`, `watched`, `errors`, `stalled`), optionally narrowed by progress, organization or a code-prefix / custodian-name search. Returns the page, the total for the chosen filter and the count for every filter. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsQueueController_list","parameters":[{"name":"skip","required":false,"in":"query","schema":{"maximum":10000,"default":0,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"maximum":100,"default":25,"type":"number"}},{"name":"filter","required":false,"in":"query","schema":{"default":"all","type":"string","enum":["all","priority","watched","errors","stalled"]}},{"name":"progress","required":false,"in":"query","schema":{"type":"string","enum":["not_started","in_progress","not_completed"]}},{"name":"orgId","required":false,"in":"query","schema":{"type":"string"}},{"name":"q","required":false,"in":"query","schema":{"maxLength":100,"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"extractionCodeId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"code":{"type":"string"},"custodianName":{"type":"string","nullable":true},"orgId":{"type":"string","nullable":true},"caseId":{"type":"string","nullable":true},"orgPriority":{"type":"boolean"},"watched":{"type":"boolean"},"stage":{"type":"string","enum":["created","email_sent","portal_login","download_clicked","code_login","extracting","extracted","uploaded","completed"]},"stageIndex":{"type":"number"},"status":{"type":"string","enum":["active","stalled","error","completed"]},"lastEvent":{"type":"object","nullable":true,"properties":{"type":{"type":"string"},"message":{"type":"string"},"severity":{"type":"string"},"at":{"type":"string","format":"date-time"}}},"lastActivityAt":{"type":"string","format":"date-time","nullable":true},"openInboxItems":{"type":"number"},"livePercent":{"type":"number","nullable":true}}}},"total":{"type":"number","description":"Rows matching the chosen filter","example":87},"counts":{"type":"object","description":"Rows matching each filter","properties":{"all":{"type":"number","example":87},"priority":{"type":"number","example":6},"watched":{"type":"number","example":3},"errors":{"type":"number","example":9},"stalled":{"type":"number","example":4}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List the collection queue (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/queue/in-flight":{"get":{"description":"Counts collections that are not completed and were active since the first day of the previous calendar month (UTC): extracting now, signed in with the code but not started, app downloaded but not opened, and portal login only. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsQueueController_inFlight","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"extractingNow":{"type":"number","example":3},"codeLoginNotStarted":{"type":"number","example":5},"downloadClickedNotOpened":{"type":"number","example":2},"portalLoginOnly":{"type":"number","example":7}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Count in-flight collections by stage (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/live":{"get":{"description":"Lists backup and upload attempts whose last heartbeat is within the configured heartbeat-lost window, across up to 500 collections, priority organizations first, each marked `running` or `slow`. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsQueueController_listLive","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"extractionCodeId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"code":{"type":"string"},"custodianName":{"type":"string","nullable":true},"orgPriority":{"type":"boolean"},"attemptId":{"type":"string"},"activity":{"type":"string","enum":["backup","upload"]},"dataSource":{"type":"string","nullable":true,"example":"ios"},"percent":{"type":"number","nullable":true,"example":42},"rateBps":{"type":"number","nullable":true},"etaSec":{"type":"number","nullable":true},"phase":{"type":"string","nullable":true},"freeDiskBytes":{"type":"number","nullable":true},"lastHeartbeatAt":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["running","slow"]}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List running backups and uploads (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/inbox":{"get":{"description":"Lists inbox items for one status (`open` by default; `cleared` looks back `clearedSinceHours`, default 24), optionally priority-only, grouped into one card per extraction code, priority organizations first, then most recent. Returns the page of cards, the total and the number of codes per status. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsInboxController_list","parameters":[{"name":"skip","required":false,"in":"query","schema":{"maximum":10000,"default":0,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"maximum":100,"default":25,"type":"number"}},{"name":"status","required":false,"in":"query","schema":{"default":"open","type":"string","enum":["open","snoozed","cleared"]}},{"name":"priority","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"clearedSinceHours","required":false,"in":"query","schema":{"maximum":720,"default":24,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"extractionCodeId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"code":{"type":"string","nullable":true},"custodianName":{"type":"string","nullable":true},"orgId":{"type":"string","nullable":true},"caseId":{"type":"string","nullable":true},"orgPriority":{"type":"boolean"},"stage":{"type":"string","nullable":true,"enum":["created","email_sent","portal_login","download_clicked","code_login","extracting","extracted","uploaded","completed"]},"status":{"type":"string","nullable":true,"enum":["active","stalled","error","completed"]},"lastTriggeredAt":{"type":"string","format":"date-time"},"recovered":{"type":"boolean","description":"Every item on the card has a later success"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"reason":{"type":"string","enum":["backup_failed","upload_failed","processing_failed","export_failed","invite_failed","extraction_too_long","upload_too_long","slow_transfer","went_silent","device_not_detected","stalled"]},"errorCode":{"type":"string","nullable":true,"example":"DEVICE_UNPLUGGED"},"summary":{"type":"string","example":"Backup failed: device unplugged"},"status":{"type":"string","enum":["open","snoozed","cleared"]},"triggerCount":{"type":"number","example":2},"lastTriggeredAt":{"type":"string","format":"date-time"},"recoveredAt":{"type":"string","format":"date-time","nullable":true},"snoozedUntil":{"type":"string","format":"date-time","nullable":true},"clearedAt":{"type":"string","format":"date-time","nullable":true},"clearedBy":{"type":"string","nullable":true,"description":"User id"}}}}}}},"total":{"type":"number","example":12},"counts":{"type":"object","description":"Number of extraction codes per status","properties":{"open":{"type":"number","example":12},"snoozed":{"type":"number","example":2},"cleared":{"type":"number","example":5}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List inbox cards (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/inbox/items/{id}/clear":{"post":{"description":"Clears an open or snoozed inbox item and records it on the timeline. Returns the updated item as stored. Returns 404 when the item is unknown or already cleared. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsInboxController_clear","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the inbox item to clear","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The inbox item as stored (raw document: `_id`, not `id`)","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"extractionCodeId":{"type":"string"},"orgId":{"type":"string","nullable":true},"caseId":{"type":"string","nullable":true},"orgPriority":{"type":"boolean"},"reason":{"type":"string","enum":["backup_failed","upload_failed","processing_failed","export_failed","invite_failed","extraction_too_long","upload_too_long","slow_transfer","went_silent","device_not_detected","stalled"]},"errorCode":{"type":"string","nullable":true},"summary":{"type":"string"},"status":{"type":"string","enum":["open","snoozed","cleared"]},"isLive":{"type":"boolean","description":"true while open or snoozed"},"firstTriggeredAt":{"type":"string","format":"date-time"},"lastTriggeredAt":{"type":"string","format":"date-time"},"triggerCount":{"type":"number"},"triggerEventIds":{"type":"array","items":{"type":"string"}},"recoveredAt":{"type":"string","format":"date-time","nullable":true},"snoozedUntil":{"type":"string","format":"date-time","nullable":true},"clearedBy":{"type":"string","nullable":true},"clearedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Clear one inbox item (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/inbox/codes/{codeId}/clear":{"post":{"description":"Clears every open or snoozed inbox item of the extraction code and returns how many were cleared. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsInboxController_clearCode","parameters":[{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"cleared":{"type":"number","example":3}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Clear all inbox items of a code (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/inbox/items/{id}/snooze":{"post":{"description":"Snoozes an open or snoozed inbox item until `until`, which must be at least 1 minute and at most 90 days ahead (400 otherwise), and records it on the timeline. Returns the updated item as stored; 404 when the item is unknown or cleared. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsInboxController_snooze","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the inbox item to snooze","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnoozeInboxItemDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The inbox item as stored (raw document: `_id`, not `id`)","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"extractionCodeId":{"type":"string"},"orgId":{"type":"string","nullable":true},"caseId":{"type":"string","nullable":true},"orgPriority":{"type":"boolean"},"reason":{"type":"string","enum":["backup_failed","upload_failed","processing_failed","export_failed","invite_failed","extraction_too_long","upload_too_long","slow_transfer","went_silent","device_not_detected","stalled"]},"errorCode":{"type":"string","nullable":true},"summary":{"type":"string"},"status":{"type":"string","enum":["open","snoozed","cleared"]},"isLive":{"type":"boolean","description":"true while open or snoozed"},"firstTriggeredAt":{"type":"string","format":"date-time"},"lastTriggeredAt":{"type":"string","format":"date-time"},"triggerCount":{"type":"number"},"triggerEventIds":{"type":"array","items":{"type":"string"}},"recoveredAt":{"type":"string","format":"date-time","nullable":true},"snoozedUntil":{"type":"string","format":"date-time","nullable":true},"clearedBy":{"type":"string","nullable":true},"clearedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Snooze an inbox item (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/inbox/items/{id}/unsnooze":{"post":{"description":"Reopens a snoozed inbox item now. Returns the updated item as stored; 404 when the item is not snoozed. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsInboxController_unsnooze","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the inbox item to unsnooze","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The inbox item as stored (raw document: `_id`, not `id`)","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"extractionCodeId":{"type":"string"},"orgId":{"type":"string","nullable":true},"caseId":{"type":"string","nullable":true},"orgPriority":{"type":"boolean"},"reason":{"type":"string","enum":["backup_failed","upload_failed","processing_failed","export_failed","invite_failed","extraction_too_long","upload_too_long","slow_transfer","went_silent","device_not_detected","stalled"]},"errorCode":{"type":"string","nullable":true},"summary":{"type":"string"},"status":{"type":"string","enum":["open","snoozed","cleared"]},"isLive":{"type":"boolean","description":"true while open or snoozed"},"firstTriggeredAt":{"type":"string","format":"date-time"},"lastTriggeredAt":{"type":"string","format":"date-time"},"triggerCount":{"type":"number"},"triggerEventIds":{"type":"array","items":{"type":"string"}},"recoveredAt":{"type":"string","format":"date-time","nullable":true},"snoozedUntil":{"type":"string","format":"date-time","nullable":true},"clearedBy":{"type":"string","nullable":true},"clearedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Wake a snoozed inbox item (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/collections/{codeId}":{"get":{"description":"Returns one extraction code's collection state: header (code, custodian, stage, status, requester and case), stage bar, per-source state, live backup/upload attempts, the last computer seen, up to 50 live inbox items and per-requested-source progress. Returns 404 when the code has no collection state. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsCollectionsController_get","parameters":[{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"header":{"type":"object","properties":{"extractionCodeId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"code":{"type":"string","example":"K7Q2ZP"},"custodianName":{"type":"string","nullable":true,"example":"Jane Doe"},"orgId":{"type":"string","nullable":true},"caseId":{"type":"string","nullable":true},"orgPriority":{"type":"boolean"},"watched":{"type":"boolean"},"stage":{"type":"string","enum":["created","email_sent","portal_login","download_clicked","code_login","extracting","extracted","uploaded","completed"]},"status":{"type":"string","enum":["active","stalled","error","completed"]},"lifecycleMs":{"type":"number","nullable":true,"description":"From creation to completion, or to now while open"},"completedAt":{"type":"string","format":"date-time","nullable":true},"failedAttempts":{"type":"number"},"requestedBy":{"type":"object","nullable":true,"properties":{"userId":{"type":"string"},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true,"example":"paralegal@example.com"}}},"caseName":{"type":"string","nullable":true,"example":"Smith v. Jones"}}},"stageBar":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"string"},"tracked":{"type":"boolean"},"reached":{"type":"boolean"},"at":{"type":"string","format":"date-time","nullable":true}}}},"sources":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","example":"ios:00008110-001A2B3C4D5E6F70"},"dataSource":{"type":"string","nullable":true,"example":"ios"},"stage":{"type":"string","enum":["created","email_sent","portal_login","download_clicked","code_login","extracting","extracted","uploaded","completed"]},"stageOrder":{"type":"number"},"status":{"type":"string","enum":["active","error","completed"]},"lastAt":{"type":"string","format":"date-time"},"lastErrorCode":{"type":"string","nullable":true}}}},"live":{"type":"array","items":{"type":"object","properties":{"attemptId":{"type":"string"},"activity":{"type":"string","enum":["backup","upload"]},"dataSource":{"type":"string","nullable":true},"deviceId":{"type":"string","nullable":true},"systemId":{"type":"string","nullable":true},"percent":{"type":"number","nullable":true},"bytesDone":{"type":"number","nullable":true},"bytesTotal":{"type":"number","nullable":true},"rateBps":{"type":"number","nullable":true},"etaSec":{"type":"number","nullable":true},"phase":{"type":"string","nullable":true},"freeDiskBytes":{"type":"number","nullable":true},"slowSince":{"type":"string","format":"date-time","nullable":true},"startedAt":{"type":"string","format":"date-time","nullable":true},"lastHeartbeatAt":{"type":"string","format":"date-time"}}}},"computer":{"type":"object","nullable":true,"properties":{"systemId":{"type":"string","nullable":true},"appVersion":{"type":"string","nullable":true,"example":"3.14.0"},"os":{"type":"string","nullable":true},"model":{"type":"string","nullable":true},"totalMemBytes":{"type":"number","nullable":true},"freeDiskBytes":{"type":"number","nullable":true},"at":{"type":"string","format":"date-time","nullable":true}}},"openItems":{"type":"array","description":"Live inbox items, newest first, at most 50","items":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"reason":{"type":"string","enum":["backup_failed","upload_failed","processing_failed","export_failed","invite_failed","extraction_too_long","upload_too_long","slow_transfer","went_silent","device_not_detected","stalled"]},"errorCode":{"type":"string","nullable":true,"example":"DEVICE_UNPLUGGED"},"summary":{"type":"string","example":"Backup failed: device unplugged"},"status":{"type":"string","enum":["open","snoozed","cleared"]},"triggerCount":{"type":"number","example":2},"lastTriggeredAt":{"type":"string","format":"date-time"},"recoveredAt":{"type":"string","format":"date-time","nullable":true},"snoozedUntil":{"type":"string","format":"date-time","nullable":true},"clearedAt":{"type":"string","format":"date-time","nullable":true},"clearedBy":{"type":"string","nullable":true,"description":"User id"}}}},"requestedSources":{"type":"array","items":{"type":"object","properties":{"dataSource":{"type":"string","example":"gmail"},"sourceType":{"type":"string"},"status":{"type":"string","enum":["pending","in_progress","failed","completed"]},"rows":{"type":"number"},"completedRows":{"type":"number"}}}},"sourcesProgress":{"type":"object","properties":{"requested":{"type":"number","example":3},"completed":{"type":"number","example":1}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get one collection's detail (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/collections/{codeId}/timeline":{"get":{"description":"Returns a page of the code's collection events, newest first unless `order=asc`, filterable by event types and severities, with the total. Attachments are listed by index only; a download link is minted per attachment by its own route. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsCollectionsController_list","parameters":[{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}},{"name":"skip","required":false,"in":"query","schema":{"maximum":10000,"default":0,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"maximum":100,"default":25,"type":"number"}},{"name":"order","required":false,"in":"query","schema":{"default":"desc","type":"string","enum":["desc","asc"]}},{"name":"types","required":false,"in":"query","description":"Comma-separated event types.","schema":{"type":"array","items":{"type":"string"}}},{"name":"severity","required":false,"in":"query","description":"Comma-separated severities.","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"eventId":{"type":"string"},"type":{"type":"string","example":"backup.failed"},"source":{"type":"string","enum":["api","app","portal","ops","system"]},"severity":{"type":"string","enum":["info","warning","error"]},"message":{"type":"string"},"occurredAt":{"type":"string","format":"date-time"},"receivedAt":{"type":"string","format":"date-time"},"clockSkewMs":{"type":"number","nullable":true},"count":{"type":"number","description":"Repeats collapsed into this row"},"firstAt":{"type":"string","format":"date-time"},"lastAt":{"type":"string","format":"date-time"},"dataSource":{"type":"string","nullable":true},"deviceId":{"type":"string","nullable":true},"systemId":{"type":"string","nullable":true},"attemptId":{"type":"string","nullable":true},"errorCode":{"type":"string","nullable":true},"error":{"type":"object","nullable":true,"properties":{"title":{"type":"string","example":"Device unplugged"},"explanation":{"type":"string"},"category":{"type":"string","enum":["connection","disk","permission","device","encryption","server","unknown"]},"severity":{"type":"string","enum":["info","warning","error"]}}},"details":{"type":"object","nullable":true,"additionalProperties":true},"attachments":{"type":"array","items":{"type":"object","properties":{"index":{"type":"number"},"kind":{"type":"string"},"sizeBytes":{"type":"number","nullable":true}}}},"origin":{"type":"string","example":"v2"},"actorUserId":{"type":"string","nullable":true}}}},"total":{"type":"number","example":140}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a collection's event timeline (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/collections/{codeId}/attachments/{eventId}/{index}":{"get":{"description":"Returns a signed URL, valid for 15 minutes, for one attachment of a collection event. Returns 404 when the event does not belong to the code, the index has no attachment or the file is unavailable. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsCollectionsController_attachment","parameters":[{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}},{"name":"eventId","required":true,"in":"path","description":"Id of the collection event","schema":{"type":"string"}},{"name":"index","required":true,"in":"path","description":"Attachment index on the event","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["url","expiresAt"],"properties":{"url":{"type":"string","example":"https://bucket.s3.amazonaws.com/logs/app-log.zip?X-Amz-Signature=abc123"},"expiresAt":{"type":"string","format":"date-time","example":"2026-01-15T10:30:00.000Z"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a download link for an event attachment (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/collections/{codeId}/request-logs":{"post":{"description":"Asks up to 10 online desktop apps on the code for their app log and one device log per phone source, and records the request on the timeline. Returns how many computers were asked and how many device-log requests went out; 404 for an unknown code. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsCollectionsController_request","parameters":[{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"computers":{"type":"number","example":1},"deviceLogRequests":{"type":"number","example":2}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Ask the custodian's computers for logs (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/collections/{codeId}/watch":{"patch":{"description":"Adds the extraction code to the watch list or removes it, and returns the new value. Returns 404 for an unknown code. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsCollectionsController_watch","parameters":[{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetWatchedDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"watched":{"type":"boolean","example":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Watch or unwatch a collection (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/organizations/{id}/priority":{"patch":{"description":"Sets or clears the organization's priority flag and copies it to its collections and live inbox items. Returns the new value and how many collections changed; 404 for an unknown organization. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsCollectionsController_priority","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetOrgPriorityDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"isPriority":{"type":"boolean","example":true},"statesUpdated":{"type":"number","example":4}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Mark an organization as priority (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/stats":{"get":{"description":"Returns stats for collections created in a date range (default the last 30 days, at most 366; 400 when `from` is not before `to`), optionally for one organization: cohort size and completion rate, lifecycle times, the stage funnel, median time in each stage, phone-only portal logins, requested-source progress and current open inbox items by reason. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsStatsController_get","parameters":[{"name":"from","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"to","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"orgId","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"cohortSize":{"type":"number","example":120},"completed":{"type":"number","example":96},"completionRate":{"type":"number","nullable":true,"example":0.8},"lifecycle":{"type":"object","properties":{"avgMs":{"type":"number","nullable":true},"medianMs":{"type":"number","nullable":true}}},"funnel":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"string"},"tracked":{"type":"boolean"},"count":{"type":"number","nullable":true},"drop":{"type":"number","nullable":true}}}},"medianTimeInStage":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"medianMs":{"type":"number","nullable":true},"samples":{"type":"number"},"longest":{"type":"boolean"}}}},"phoneOnlyPortalLogins":{"type":"number"},"sourcesRequested":{"type":"number"},"sourcesCompleted":{"type":"number"},"codesWithRequestedSources":{"type":"number"},"fullyCompleted":{"type":"number"},"fullCompletionRate":{"type":"number","nullable":true},"bySourceType":{"type":"array","items":{"type":"object","properties":{"sourceType":{"type":"string"},"requested":{"type":"number"},"completed":{"type":"number"}}}},"needsAttention":{"type":"object","description":"Open inbox items now, not limited to the date range","properties":{"total":{"type":"number","example":9},"byReason":{"type":"object","additionalProperties":{"type":"number"},"example":{"backup_failed":4,"went_silent":5}}}},"truncated":{"type":"boolean","description":"The cohort hit the 50,000-row cap"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get collection funnel and lifecycle stats (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/errors/{errorCode}/stats":{"get":{"description":"For one error code over the last `days` (default 30, at most 365): its catalog entry, how many times it fired, how many extraction codes it affected and how many of those later completed. Returns 400 for a malformed code. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsStatsController_errorStats","parameters":[{"name":"errorCode","required":true,"in":"path","description":"Raw error code, e.g. DEVICE_UNPLUGGED","schema":{"type":"string"}},{"name":"days","required":false,"in":"query","schema":{"minimum":1,"maximum":365,"default":30,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"errorCode":{"type":"string","example":"DEVICE_UNPLUGGED"},"days":{"type":"number","example":30},"error":{"type":"object","properties":{"title":{"type":"string","example":"Device unplugged"},"explanation":{"type":"string"},"category":{"type":"string","enum":["connection","disk","permission","device","encryption","server","unknown"]},"severity":{"type":"string","enum":["info","warning","error"]}}},"hits":{"type":"number","example":17},"codesAffected":{"type":"number","example":9},"laterCompleted":{"type":"number","example":6},"neverCompleted":{"type":"number","example":3}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get stats for one error code (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/collections/{codeId}/timeline.md":{"get":{"description":"Returns the code's timeline (the newest 2,000 events, oldest first) as a markdown document, with event messages passed through PII redaction and signed links to attachments. Returns 404 when the code has no collection state. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsMarkdownController_markdown","parameters":[{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}}],"responses":{"200":{"description":"Markdown document (text/markdown). Not wrapped in the JSON envelope.","content":{"text/markdown":{"schema":{"type":"string"}}}}},"security":[{"access-token":[]}],"summary":"Get a collection timeline as markdown (internal)","tags":["admin"],"x-internal":true}},"/admin/collection-ops/collections/{codeId}/claude-link":{"post":{"description":"Mints a signed, read-only link to the code's markdown timeline, valid for the configured number of days (7 by default, at most 30), and records it on the timeline. A link cannot be revoked on its own. Returns 503 when the link secret is not configured and 404 when the code has no collection state. Super admin only; 404 while the collection-ops console is turned off.","operationId":"CollectionOpsMarkdownController_claudeLink","parameters":[{"name":"codeId","required":true,"in":"path","description":"Id of the extraction code","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["url","expiresAt"],"properties":{"url":{"type":"string","example":"https://api.example.com/public/collection-ops/timeline/eyJjIjoiNjVhMWIyYzMifQ.c2lnbmF0dXJl"},"expiresAt":{"type":"string","format":"date-time","example":"2026-01-15T10:30:00.000Z"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a read-only link to a collection timeline (internal)","tags":["admin"],"x-internal":true}},"/public/collection-ops/timeline/{token}":{"get":{"description":"Returns the markdown timeline for a link minted by the timeline-link route. No auth; limited to 30 requests per minute. A bad, forged or expired token, an unconfigured secret, the console being off and an unknown collection all return the same 404.","operationId":"CollectionOpsPublicController_timeline","parameters":[{"name":"token","required":true,"in":"path","description":"Signed link token","schema":{"type":"string"}}],"responses":{"200":{"description":"Markdown document (text/markdown). Not wrapped in the JSON envelope.","content":{"text/markdown":{"schema":{"type":"string"}}}}},"summary":"Read a collection timeline from a shared link (internal)","tags":["public"],"x-internal":true}},"/certificates":{"post":{"description":"Stores a collection certificate for a case: its data, the PDF and JSON files (hashed so uploads can be verified later) and the source ids it covers, and records which side created it. Opposing counsel may create one only while that feature is turned on (403 otherwise). Returns 400 when the certificate id already exists and 404 for an unknown case. Requires a role in the organization and access to the case.","operationId":"CertificateController_create","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCertificateDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CertificateEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Store a certificate","tags":["certificates"]},"get":{"description":"Lists the organization's certificates for the case in `caseId`, newest first, optionally only those covering `sourceId`, paged with `page` and `perPage` (10 per page by default). Requires a role in the organization.","operationId":"CertificateController_list","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"caseId","required":true,"in":"query","schema":{"type":"string"}},{"name":"sourceId","required":false,"in":"query","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CertificateListEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List the certificates of a case","tags":["certificates"]}},"/certificates/verify-document":{"post":{"description":"Public, no sign-in; at most 10 requests a minute. Checks an uploaded certificate PDF or JSON against the stored certificate (file hash, plus the PDF signature or the JSON content) and returns `authentic`, `tampered` or `revoked` with a message. Returns 400 for a missing file or another file type and 404 when the file names no known certificate.","operationId":"CertificateController_verifyDocument","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DocumentVerificationEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Verify a certificate file","tags":["certificates"]}},"/certificates/{certificateId}/verify":{"get":{"description":"Public, no sign-in; at most 20 requests a minute. Returns the public details of a certificate: status, which side created it, collection and issue dates, custodian, device, item and message counts, date range and data hash. Returns 404 for an unknown id.","operationId":"CertificateController_verify","parameters":[{"name":"certificateId","required":true,"in":"path","description":"Certificate ID (e.g., HC-2026-05-15-A1B2-1F3)","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CertificateVerificationEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}},"404":{"description":"Certificate not found"}},"security":[{"access-token":[]}],"summary":"Verify a certificate by id","tags":["certificates"]}},"/certificates/{certificateId}/pdf":{"get":{"description":"Returns a signed, time-limited download link to the certificate PDF. Returns 404 when the organization has no such certificate or the file cannot be fetched. Requires a role in the organization.","operationId":"CertificateController_getPdfUrl","parameters":[{"name":"certificateId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"url":{"type":"string","example":"https://user-data.s3.amazonaws.com/certificates/HC-2026-05-15-A1B2-1F3.pdf?X-Amz-Signature=abc123"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the certificate PDF link","tags":["certificates"]}},"/certificates/{certificateId}/json":{"get":{"description":"Returns a signed, time-limited download link to the certificate JSON. Returns 404 when the organization has no such certificate or the file cannot be fetched. Requires a role in the organization.","operationId":"CertificateController_getJsonUrl","parameters":[{"name":"certificateId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"url":{"type":"string","example":"https://user-data.s3.amazonaws.com/certificates/HC-2026-05-15-A1B2-1F3.json?X-Amz-Signature=abc123"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the certificate JSON link","tags":["certificates"]}},"/certificates/{certificateId}":{"patch":{"description":"Marks the certificate revoked with `revocationReason` and the time, so verification reports it as revoked from then on. Returns 404 when the organization has no such certificate. Requires an org admin or owner.","operationId":"CertificateController_revoke","parameters":[{"name":"certificateId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeCertificateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CertificateEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Revoke a certificate","tags":["certificates"]}},"/conversation/bulk-import":{"post":{"description":"Desktop upload plumbing: imports a batch of conversations and messages for a device. The desktop app uses the /extraction twin of this route.","operationId":"ConversationController_bulkUpload","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Bulk upload conversation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadBulkConversationDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Bulk-import conversations (internal)","tags":["conversation"],"x-internal":true}},"/conversation/search":{"get":{"description":"Lists conversations in the caller's cases (or one case), with their participants, message counts and dates, optionally filtered. On cases shared with opposing counsel, only conversations the caller may see are returned.","operationId":"ConversationController_searchConversations","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"case","required":false,"in":"query","schema":{"type":"string"}},{"name":"conversationIds","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"source","required":false,"in":"query","schema":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"]}},{"name":"sources","required":false,"in":"query","schema":{"type":"array","items":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"]}}},{"name":"linkedContacts","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"devices","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"name","type":"string","enum":["createdAt","updatedAt","name","lastMessageDate"]}},{"name":"hideExported","required":false,"in":"query","description":"0 false, 1 true, default 0 (false)","schema":{"default":0,"type":"number"}},{"name":"isArchived","required":false,"in":"query","description":"0 false, 1 true, default 0 (false)","schema":{"default":0,"type":"number"}},{"name":"isStarred","required":false,"in":"query","description":"0 false, 1 true, default 0 (false)","schema":{"default":0,"type":"number"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceConversationEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List conversations","tags":["conversation"]}},"/conversation/search-entries":{"get":{"description":"Lists messages for the selected conversations of a case, with attachments, tags and comment counts. Dates are the device's wall-clock time labelled UTC.","operationId":"ConversationController_searchConversationEntries","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"case","required":false,"in":"query","description":"Required if caseSlug not present","schema":{"type":"string"}},{"name":"caseSlug","required":false,"in":"query","description":"Required if case not present","schema":{"type":"string"}},{"name":"conversation","required":false,"in":"query","description":"ID of the conversation to search for","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeTags","required":false,"in":"query","description":"Exclude messages carrying any of these tag ids. Must not overlap `tags`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"untagged","required":false,"in":"query","description":"When truthy (1/true), return only messages with no tags attached. Takes precedence over the `tags`/`excludeTags` filters.","schema":{"type":"boolean"}},{"name":"source","required":false,"in":"query","schema":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"]}},{"name":"sources","required":false,"in":"query","schema":{"type":"array","items":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"]}}},{"name":"linkedContacts","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"devices","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"startDate","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"endDate","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"hasAttachments","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"messageIndex","type":"string","enum":["sentAt","date","messageIndex"]}},{"name":"sharedStatus","required":false,"in":"query","description":"Opposing-council only: filter to entries whose ocSharedStatus matches (e.g. `pending` for \"show me what I have not shared yet\"). Ignored for non-OC callers.","schema":{"type":"string","enum":["shared","pending","not_gated"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SearchEntryMessageResponseEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List messages in a conversation","tags":["conversation"]}},"/conversation/compare":{"post":{"description":"Merges messages from several conversations into one timeline, grouped by minute (up to 200 groups per page). Pass the returned `next` token to get the following page.","operationId":"ConversationController_compareConversationsEntries","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompareConversationMessagesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"next":{"type":"string","nullable":true,"description":"Opaque cursor for the next page; null on the last page","example":"eyJkYXRlIjoiMjAyNi0wMS0xNVQxNDozMTowMC4wMDBaIn0"},"data":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string","format":"date-time","description":"Start of the minute"},"date":{"type":"string","format":"date-time"},"count":{"type":"number","example":3},"messages":{"type":"array","items":{"$ref":"#/components/schemas/CompareDeviceMessageResponseEntity"}}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Compare conversations side by side","tags":["conversation"]}},"/conversation/search-fuzzy":{"get":{"description":"Finds messages in a case whose text matches the search text, with the match highlighted.","operationId":"ConversationController_searchConversationEntriesText","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"case","required":false,"in":"query","description":"Required if caseSlug not present","schema":{"type":"string"}},{"name":"caseSlug","required":false,"in":"query","description":"Required if case not present","schema":{"type":"string"}},{"name":"conversation","required":false,"in":"query","description":"ID of the conversation to search for","schema":{"type":"string"}},{"name":"searchText","required":true,"in":"query","schema":{"minLength":3,"maxLength":100,"type":"string"}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"sources","required":false,"in":"query","schema":{"type":"array","items":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"]}}},{"name":"linkedContacts","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"devices","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"startDate","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"endDate","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"bbCode","required":false,"in":"query","schema":{"default":"highlight=\"#FFFF00\"","type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceMessageTextSearchResponse"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search message text","tags":["conversation"]}},"/conversation/search-advance":{"post":{"description":"Finds messages in a case matching keyword conditions, with the matches highlighted.","operationId":"ConversationController_searchConversationEntriesAdvance","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvanceTextSearchDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceMessageTextSearchResponse"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Advanced message search","tags":["conversation"]}},"/conversation/{caseId}/tags-count":{"post":{"description":"Returns how many messages in the case carry each tag, for the given filters.","operationId":"ConversationController_getMessageTagsCount","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTagsCountDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageTagsCountResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Count messages per tag","tags":["conversation"]}},"/conversation/search-uploaded-conversations":{"get":{"description":"Desktop upload plumbing: lists conversations already uploaded for a device. The desktop app uses the /extraction twin of this route.","operationId":"ConversationController_searchUploadedConversation","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"case","required":true,"in":"query","schema":{"type":"string"}},{"name":"deviceExternalId","required":false,"in":"query","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UploadedConversation"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List uploaded conversations (internal)","tags":["conversation"],"x-internal":true}},"/conversation/{conversationId}/nearest-message-by-date":{"get":{"description":"Returns the id, position and date of the message in a conversation closest to the given date, so a client can jump to it.","operationId":"ConversationController_searchNearestMessageByDate","parameters":[{"name":"date","required":true,"in":"query","description":"Date to search","schema":{"type":"string"}},{"name":"case","required":true,"in":"query","description":"Case id to search for messages","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/SearchMessageByDateResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Find the message nearest a date","tags":["conversation"]}},"/conversation/entries/tags/{caseId}/bulk":{"post":{"description":"Applies tags to many messages at once and returns the updated messages.","operationId":"ConversationEntriesTagsController_bulkCreate","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Tags to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBulkMessageTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UpdateBulkMessageTagsResultEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag many messages","tags":["conversation"]}},"/conversation/entries/tags/{caseId}/{messageId}":{"post":{"description":"Adds tags to one message and returns it.","operationId":"ConversationEntriesTagsController_create","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"messageId","required":true,"in":"path","description":"Id of the message","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Tags to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceMessageEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag a message","tags":["conversation"]},"delete":{"description":"Removes tags from one message and returns it.","operationId":"ConversationEntriesTagsController_remove","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"messageId","required":true,"in":"path","description":"Id of the message","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Tags to remove","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveMessageTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceMessageEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Untag a message","tags":["conversation"]}},"/aws/s3-user-data-upload-credentials":{"post":{"description":"Returns temporary AWS credentials, with the bucket, region and API version, for uploading the caller's data to S3. The organization named by `orgId` or `organization` in the body or query, or by the `organization` header, must have an active, paid plan; otherwise 403. Requires membership of the organization in the `organization` header (403 otherwise).","operationId":"AwsController_getTemporaryUserDataUploadToken","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AwsTemporaryS3CredentialEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get temporary S3 upload credentials","tags":["aws"]}},"/common/file/add":{"post":{"description":"Desktop plumbing: registers an object already in the user-data bucket as a file record (name from the key, content type from `type` or from S3) and returns it; a key that is already registered returns the existing record. Returns 404 when the key is not in the bucket. Keys in folders the API writes itself (email bodies, certificates, statements, exports, logs, device backups) cannot be registered (403).","operationId":"CommonFileController_addFile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddFileDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserFileEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Register an uploaded S3 object as a file (internal)","tags":["common"],"x-internal":true}},"/common/file/{fileId}/signed-url":{"get":{"description":"Returns a signed download URL for a file and the time it is valid until (up to 24 hours). Returns 404 for an unknown or non-S3 file.","operationId":"CommonFileController_getSignedUrl","parameters":[{"name":"fileId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["url","expiresAt"],"properties":{"url":{"type":"string","example":"https://bucket.s3.amazonaws.com/files/report.pdf?X-Amz-Signature=abc123"},"expiresAt":{"type":"string","format":"date-time","example":"2026-01-15T10:30:00.000Z"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a signed download URL for a file","tags":["common"]}},"/common/file/amd-driver/{systemType}":{"get":{"description":"Desktop plumbing: streams the Apple Mobile Device Support installer for Windows; `x64` gets the 64-bit build and any other value the 32-bit one. No auth. Returns 404 when the file cannot be streamed.","operationId":"CommonFileController_getAmdFile","parameters":[{"name":"systemType","required":true,"in":"path","description":"`x64` for the 64-bit installer; any other value returns the 32-bit one","schema":{"type":"string"}}],"responses":{"200":{"description":"The .msi installer. Not wrapped in the JSON envelope.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}}},"summary":"Download the Apple Mobile Device Support installer (internal)","tags":["common"],"x-internal":true}},"/common/file/{fileId}":{"get":{"description":"Downloads a file by id. No auth. Usually answers 307 with a redirect to a signed storage URL of the original; otherwise streams the bytes. Returns 404 when the stored original is missing.","operationId":"CommonFileController_getFile","parameters":[{"name":"fileId","required":true,"in":"path","description":"Id of the file","schema":{"type":"string"}}],"responses":{"200":{"description":"The file bytes, streamed when no signed URL is available. Not wrapped in the JSON envelope.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"307":{"description":"Usual answer: redirect (Location header) to a short-lived signed storage URL of the original file."}},"summary":"Download a file","tags":["common"]}},"/common/file/{fileId}/original/{fileName}":{"get":{"description":"Downloads a file by id. No auth. Usually answers 307 with a redirect to a signed storage URL of the original; otherwise streams the bytes. Returns 404 when the stored original is missing. The trailing file name is ignored.","operationId":"CommonFileController_getOriginalFileWithName","parameters":[{"name":"fileId","required":true,"in":"path","description":"Id of the file","schema":{"type":"string"}},{"name":"fileName","required":true,"in":"path","description":"Any file name; ignored, it only gives the URL a readable name","schema":{}}],"responses":{"200":{"description":"The file bytes, streamed when no signed URL is available. Not wrapped in the JSON envelope.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"307":{"description":"Usual answer: redirect (Location header) to a short-lived signed storage URL of the original file."}},"summary":"Download a file's original, with a file name in the URL","tags":["common"]}},"/common/file/{fileId}/display/{fileName}":{"get":{"description":"Downloads the browser-displayable copy of a file (for example HEIC converted to PNG, or audio converted to MP3). No auth. Redirects (307) to that copy when it exists; otherwise redirects to the original and, for convertible types, queues the conversion so the copy exists next time. Streams the bytes when no signed URL is available. Returns 404 when neither copy is available.","operationId":"CommonFileController_getDisplayableFileWithName","parameters":[{"name":"fileId","required":true,"in":"path","description":"Id of the file","schema":{"type":"string"}},{"name":"fileName","required":true,"in":"path","description":"Any file name; ignored, it only gives the URL a readable name","schema":{}}],"responses":{"200":{"description":"The file bytes, streamed when no signed URL is available. Not wrapped in the JSON envelope.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"307":{"description":"Usual answer: redirect (Location header) to a short-lived signed storage URL of the displayable copy, or of the original when the copy is missing."}},"summary":"Download a file's browser-displayable version","tags":["common"]}},"/common/file/{fileId}/{fileName}":{"get":{"description":"Downloads a file by id. No auth. Usually answers 307 with a redirect to a signed storage URL of the original; otherwise streams the bytes. Returns 404 when the stored original is missing. The trailing file name is ignored.","operationId":"CommonFileController_getFileWithName","parameters":[{"name":"fileId","required":true,"in":"path","description":"Id of the file","schema":{"type":"string"}},{"name":"fileName","required":true,"in":"path","description":"Any file name; ignored, it only gives the URL a readable name","schema":{}}],"responses":{"200":{"description":"The file bytes, streamed when no signed URL is available. Not wrapped in the JSON envelope.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"307":{"description":"Usual answer: redirect (Location header) to a short-lived signed storage URL of the original file."}},"summary":"Download a file, with a file name in the URL","tags":["common"]}},"/application/latest":{"get":{"description":"Returns the latest desktop app version and installer files (URL, SHA-512 and size) for macOS and Windows; each file also carries a full `downloadLink`. `organization` selects a white-label build; an unknown or missing value returns the standard app. No auth.","operationId":"ApplicationController_latestAppLinks","parameters":[{"name":"organization","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LatestAppLinksEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"summary":"Get the latest desktop app downloads","tags":["application"]}},"/application/refresh":{"get":{"description":"CI hook: clears and re-reads the cached app links for a white-label build (`organization`, default the standard app), invalidates the CDN and, when `referrer=ci` and `type` is `mac` or `windows`, announces the new build internally. No auth. Always reports success, even when a step failed.","operationId":"ApplicationController_refreshAppLinks","parameters":[{"name":"referrer","required":false,"in":"query","schema":{"type":"string"}},{"name":"type","required":false,"in":"query","schema":{"type":"string"}},{"name":"organization","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"summary":"Refresh the cached desktop app links (internal)","tags":["application"],"x-internal":true}},"/application/refresh-android":{"get":{"description":"Invalidates the CDN path of the Android APK. No auth. A failure comes back as 200 with `isSuccess: false` and the error message.","operationId":"ApplicationController_refreshAndroidAppLink","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"summary":"Refresh the Android app download (internal)","tags":["application"],"x-internal":true}},"/device/file-hash":{"post":{"description":"Desktop plumbing: stores the hashes of a device's backup files.","operationId":"DeviceController_addUpdateFileHash","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUpdateDeviceFileHashDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Record device file hashes (internal)","tags":["devices"],"x-internal":true}},"/device/match-file-hash":{"post":{"description":"Desktop plumbing: checks a list of backup files against the hashes recorded for the device. Currently always returns matched: true (known issue).","operationId":"DeviceController_matchFileHash","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchDeviceFileHashDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MatchDeviceFileHashResultEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Check a device file hash (internal)","tags":["devices"],"x-internal":true}},"/device/{deviceId}":{"get":{"description":"Returns one device, including which data types exist on it (availableData), optionally limited to one case or extraction code.","operationId":"DeviceController_getDeviceDetails","parameters":[{"name":"deviceId","required":true,"in":"path","description":"Id of the device","schema":{"type":"string"}},{"name":"caseId","required":false,"in":"query","description":"Scope availability to this case","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","description":"Scope availability to this extraction code","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a device","tags":["devices"]},"patch":{"description":"Updates a device's name, manufacturer, brand or model and returns the device.","operationId":"DeviceController_updateDeviceDetails","parameters":[{"name":"deviceId","required":true,"in":"path","description":"Id of the device","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDeviceDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update a device","tags":["devices"]}},"/device-activity-log":{"post":{"description":"Desktop plumbing: records a device activity event (plugged in, backup started or finished, upload progress). A repeat of the same event within a short window is skipped.","operationId":"DeviceActivityLogController_addActivityLog","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddDeviceActivityLogDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Record device activity (internal)","tags":["device-activity-log"],"x-internal":true}},"/comments/case/{caseId}/message/{messageId}":{"post":{"description":"Adds a comment to a message. Prefer POST /comments/case/:caseId/source/:sourceType/:sourceId, which works for every kind of source.","operationId":"CommentController_createComment","parameters":[{"name":"messageId","required":true,"in":"path","description":"Id of the message","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Created comment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommentDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CommentEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Comment on a message (legacy)","tags":["comments"]},"get":{"description":"Lists the comments on one message. Prefer the source-based route.","operationId":"CommentController_searchComments","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"parent","required":false,"in":"query","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"messages","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"messageId","required":true,"in":"path","description":"Id of the message","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommentEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a message's comments (legacy)","tags":["comments"]}},"/comments/case/{caseId}":{"get":{"description":"Lists the comments in a case, oldest first by default. On a case shared with opposing counsel, each side sees only its own side's comments.","operationId":"CommentController_searchAllCommentsByCase","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"sourceType","required":false,"in":"query","schema":{"type":"string","enum":["DeviceMessage","DeviceConversation","UserEmailData","FinancialAccount","CaseFile","UserEmailDataThread","LinkedInConversation","LinkedInJobApplication","AiChatConversation"]}},{"name":"sourceId","required":false,"in":"query","schema":{"type":"string"}},{"name":"collection","required":false,"in":"query","schema":{"type":"string"}},{"name":"conversation","required":false,"in":"query","schema":{"type":"string"}},{"name":"parent","required":false,"in":"query","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"messages","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"startMessageIndex","required":false,"in":"query","description":"Required if endMessageIndex is provided","schema":{"type":"number"}},{"name":"endMessageIndex","required":false,"in":"query","description":"Required if startMessageIndex is provided","schema":{"type":"number"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommentEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a case's comments","tags":["comments"]}},"/comments/case/{caseId}/message/{messageId}/{commentId}":{"patch":{"description":"Edits the caller's own comment on a message. Prefer PATCH /comments/case/:caseId/:commentId.","operationId":"CommentController_updateComment","parameters":[{"name":"messageId","required":true,"in":"path","description":"Id of the message","schema":{"type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Id of the comment to update","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Updated comment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCommentDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CommentEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Edit a comment (legacy)","tags":["comments"]},"post":{"description":"Replies to a comment on a message. Prefer POST /comments/case/:caseId/:commentId/reply.","operationId":"CommentController_replyToComment","parameters":[{"name":"messageId","required":true,"in":"path","description":"Id of the message","schema":{"type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Id of the comment to reply to","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"description":"New reply comment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyCommentDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CommentEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Reply to a comment (legacy)","tags":["comments"]},"delete":{"description":"Deletes the caller's own comment on a message. Prefer DELETE /comments/case/:caseId/:commentId.","operationId":"CommentController_deleteComment","parameters":[{"name":"messageId","required":true,"in":"path","description":"Id of the message","schema":{"type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Id of the comment to update","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"description":"Delete Comment (and replies)","content":{"application/json":{"schema":{"type":"string"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a comment (legacy)","tags":["comments"]}},"/comments/case/{caseId}/source/{sourceType}/{sourceId}":{"post":{"description":"Adds a comment to any commentable item in the case (a message, email, file and so on), identified by sourceType and sourceId.","operationId":"CommentController_createCommentBySource","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"sourceType","required":true,"in":"path","schema":{"enum":["DeviceMessage","DeviceConversation","UserEmailData","FinancialAccount","CaseFile","UserEmailDataThread","LinkedInConversation","LinkedInJobApplication","AiChatConversation"],"type":"string"}},{"name":"sourceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommentDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CommentEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Comment on an item","tags":["comments"]},"get":{"description":"Lists the comments on one item. On a case shared with opposing counsel, each side sees only its own side's comments.","operationId":"CommentController_searchCommentsBySource","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"parent","required":false,"in":"query","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}},{"name":"messages","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"sourceType","required":true,"in":"path","schema":{"enum":["DeviceMessage","DeviceConversation","UserEmailData","FinancialAccount","CaseFile","UserEmailDataThread","LinkedInConversation","LinkedInJobApplication","AiChatConversation"],"type":"string"}},{"name":"sourceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommentEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List an item's comments","tags":["comments"]}},"/comments/case/{caseId}/{commentId}":{"patch":{"description":"Edits the caller's own comment.","operationId":"CommentController_updateCommentById","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"commentId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCommentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CommentEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Edit a comment","tags":["comments"]},"delete":{"description":"Deletes the caller's own comment.","operationId":"CommentController_deleteCommentById","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"commentId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a comment","tags":["comments"]}},"/comments/case/{caseId}/{commentId}/reply":{"post":{"description":"Adds a reply to a comment.","operationId":"CommentController_replyToCommentById","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"commentId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyCommentDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CommentEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Reply to a comment","tags":["comments"]}},"/dashboard/{orgId}/alerts":{"get":{"description":"Lists the caller's own alert notifications in the organization, newest first, filterable by read state, case and type, with the total and the read and unread counts; skip/limit or page/perPage pagination, 10 by default. Comment notifications also carry the comment's position for deep linking. No organization role is checked; only the caller's notifications are returned.","operationId":"DashboardController_searchAlerts","parameters":[{"name":"orgId","required":true,"in":"path","description":"id of the organization","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"isRead","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"case","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"type","required":false,"in":"query","schema":{"type":"array","items":{"type":"string","enum":["default","imported-new-conversation","added-conversation-to-existing-case","replied-to-comment","mentioned-in-comment","added-to-case","added-to-organization","new-comment","oc-suggestion-created-or-updated","oc-suggestion-approved","oc-suggestion-rejected","oc-suggestion-message","oc-data-approved","oc-member-added","oc-member-removed"]}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserNotificationsEntity"}},"noOfReadItems":{"type":"number","example":12},"noOfUnreadItems":{"type":"number","example":3}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List my alerts (internal)","tags":["dashboard"],"x-internal":true}},"/dashboard/{orgId}/updates":{"get":{"description":"Lists the caller's own update notifications in the organization, newest first, filterable by read state, case and type, with the total and the read and unread counts; skip/limit or page/perPage pagination, 10 by default. Comment notifications also carry the comment's position for deep linking. No organization role is checked; only the caller's notifications are returned.","operationId":"DashboardController_searchUpdates","parameters":[{"name":"orgId","required":true,"in":"path","description":"id of the organization","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"isRead","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"case","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"type","required":false,"in":"query","schema":{"type":"array","items":{"type":"string","enum":["default","imported-new-conversation","added-conversation-to-existing-case","replied-to-comment","mentioned-in-comment","added-to-case","added-to-organization","new-comment","oc-suggestion-created-or-updated","oc-suggestion-approved","oc-suggestion-rejected","oc-suggestion-message","oc-data-approved","oc-member-added","oc-member-removed"]}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserNotificationsEntity"}},"noOfReadItems":{"type":"number","example":12},"noOfUnreadItems":{"type":"number","example":3}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List my updates (internal)","tags":["dashboard"],"x-internal":true}},"/dashboard/{orgId}/mark-as-read/{notificationId}":{"patch":{"description":"Marks one of the caller's notifications in the organization as read. Reports success even when no notification matched.","operationId":"DashboardController_markAsRead","parameters":[{"name":"notificationId","required":true,"in":"path","description":"id of the notification","schema":{"type":"string"}},{"name":"orgId","required":true,"in":"path","description":"id of the organization","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Mark a notification as read (internal)","tags":["dashboard"],"x-internal":true}},"/dashboard/{orgId}/mark-all-as-read":{"patch":{"description":"Marks every notification of the caller in the organization as read.","operationId":"DashboardController_markAllAsRead","parameters":[{"name":"orgId","required":true,"in":"path","description":"id of the organization","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Mark all notifications as read (internal)","tags":["dashboard"],"x-internal":true}},"/webhooks/stripe/invoice-charge":{"post":{"description":"Receives Stripe invoice and charge webhook events. The Stripe signature is verified against the raw body; an invalid signature or payload returns 400. With webhook hardening on, each event is processed once and a retryable failure returns 500 so Stripe retries; otherwise a processing failure is logged and still answered with 200.","operationId":"StripeWebhookController_processStripeWebhookInvoiceCharges","parameters":[],"responses":{"200":{"description":"The event was accepted (or was already processed).","content":{"application/json":{"schema":{"type":"object","example":{"received":true}}}}}},"summary":"Stripe invoice and charge events (internal)","tags":["StripeWebhook"],"x-internal":true}},"/webhooks/stripe/subscriptions":{"post":{"description":"Receives Stripe subscription webhook events. The Stripe signature is verified against the raw body; an invalid signature or payload returns 400. With webhook hardening on, each event is processed once and a retryable failure returns 500 so Stripe retries; otherwise a processing failure is logged and still answered with 200.","operationId":"StripeWebhookController_processStripeWebhookSubscriptions","parameters":[],"responses":{"200":{"description":"The event was accepted (or was already processed).","content":{"application/json":{"schema":{"type":"object","example":{"received":true}}}}}},"summary":"Stripe subscription events (internal)","tags":["StripeWebhook"],"x-internal":true}},"/payments/plans":{"get":{"deprecated":true,"description":"Legacy subscription model, replaced by GET /billing/plans and GET /billing/plan/current. Lists the legacy Stripe plans with prices and which one is current, plus the attached payment method, the upcoming invoice (null without a subscription) and `billingAlert` (null when billing is healthy, otherwise the past-due and blocked state). Requires an org admin, owner or billing role.","operationId":"PaymentsController_getAvailablePlans","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AvailablePlansListEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List legacy subscription plans (internal)","tags":["payments"],"x-internal":true}},"/payments/check-coupon":{"post":{"deprecated":true,"description":"Looks up a Stripe promotion code and returns whether it can still be redeemed, its id and its discount as a percentage or a flat amount in cents. An unknown code returns `isValid: false` with the other fields null.","operationId":"PaymentsController_checkCouponValidity","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckCouponValidityDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CouponCodeDetailsResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Check a coupon code (internal)","tags":["payments"],"x-internal":true}},"/payments/checkout":{"post":{"deprecated":true,"description":"Legacy subscription model, replaced by POST /billing/plan. Creates a Stripe Checkout session for the given price, with an optional promotion code, creating the organization's Stripe customer first when needed, and returns its `url`. Requires an org admin, owner or billing role; returns 409 when the organization is billed per case.","operationId":"PaymentsController_createCheckoutSession","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutSessionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CheckoutSessionResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a legacy subscription checkout (internal)","tags":["payments"],"x-internal":true}},"/payments/upgrade/{productId}":{"post":{"deprecated":true,"description":"Legacy subscription model, replaced by POST /billing/plan. Moves the organization's active Stripe subscription to the product in `productId`, invoicing the prorated difference right away, and returns the organization details. Requires an org admin, owner or billing role; returns 409 when the organization is billed per case and 400 when it has no active subscription or is already on that plan.","operationId":"PaymentsController_subscribe","parameters":[{"name":"productId","required":true,"in":"path","description":"Id of the selected (new) plan/product","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationDetailsEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Change the legacy subscription plan (internal)","tags":["payments"],"x-internal":true}},"/payments/customer-session":{"post":{"deprecated":true,"description":"Legacy subscription model, replaced by POST /billing/setup-session. Creates a Stripe customer session for the pricing table, creating the organization's Stripe customer first when needed, and returns its client secret and expiry. Requires an org admin, owner or billing role; returns 409 when the organization is billed per case and 400 when it already has an active subscription.","operationId":"PaymentsController_customerSession","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/StripeCustomerSessionEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a legacy pricing-table session (internal)","tags":["payments"],"x-internal":true}},"/payments/billing-session":{"post":{"deprecated":true,"description":"Creates a Stripe billing portal session for the organization's legacy subscription, to update the payment method (the default `type`) or update or cancel the subscription, and returns its `url`; Stripe sends the user back to `returnUrl`. Requires an org admin, owner or billing role; any failure, including having no subscription, returns 400.","operationId":"PaymentsController_billingSession","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Create billing portal session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBillingPortalSessionDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BillingPortalResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Open the Stripe billing portal (internal)","tags":["payments"],"x-internal":true}},"/app-config":{"post":{"description":"Desktop plumbing: creates or updates the app configuration for a desktop install (systemId) and returns it with its device settings.","operationId":"AppConfigController_createOrUpdateAppConfig","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUpdateAppConfigDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AppConfigEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create or update the desktop app config (internal)","tags":["app-config"],"x-internal":true},"get":{"description":"Desktop plumbing: returns the caller's app configuration for a desktop install (systemId).","operationId":"AppConfigController_getAppConfig","parameters":[{"name":"systemId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AppConfigEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the desktop app config (internal)","tags":["app-config"],"x-internal":true}},"/app-config/device-settings":{"post":{"description":"Desktop plumbing: creates or updates the backup settings for one device on a desktop install and returns the full app config.","operationId":"AppConfigController_createOrUpdateDeviceSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUpdateAppConfigDeviceSettingsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AppConfigEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Save a device's settings (internal)","tags":["app-config"],"x-internal":true}},"/app-config/device-settings/delete":{"post":{"description":"Desktop plumbing: removes one device's settings from a desktop install and returns the remaining app config.","operationId":"AppConfigController_deleteDeviceSettings","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAppConfigDeviceSettingsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AppConfigEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a device's settings (internal)","tags":["app-config"],"x-internal":true}},"/integration/goodfact/create":{"post":{"description":"Partner route: called with the partner API key, not a user token. Finds the lawyer by `lawyerEmail` or, when there is no such user, creates the user and an organization (named from `organizationName` or the email domain) with a subscription. Then creates the case and attaches the partner integration with `goodfactEndpoint` and `caseAPIKey`, and returns that integration (type, attach date, remote case id and name).","operationId":"IntegrationController_addNewGoodFactCase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddGoodFactCaseDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseIntegrationEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"summary":"Create a case for a partner","tags":["integrations"]}},"/legal/clio/matters":{"get":{"description":"Lists a page of matters from the connected Clio account, through the caller's own Clio connection when they have one and the firm's primary connection otherwise, filtered by `status` and `query` (25 per page by default, at most 200), each marked with whether it is already linked to a case. Requires a member role or above in the organization.","operationId":"LegalController_getClioMatters","parameters":[{"name":"status","required":false,"in":"query","schema":{"enum":["open","pending","closed"],"type":"string"}},{"name":"query","required":false,"in":"query","description":"Search term to filter matters by name/description","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page number (default 1)","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (default 25, max 200)","schema":{"type":"number"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LegalMattersResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Clio matters","tags":["legal"]}},"/legal/clio/matters/{matterId}/link":{"post":{"description":"Links the case in `caseId` to a Clio matter: grants the matter's attorneys access to the case, imports them as participants and starts the first document sync. Returns the matter id, its reference number (or name) and the attorneys. Only an org admin or owner or the case creator may link it, and not an opposing counsel organization (403 otherwise). Returns 404 for an unknown case or matter and 409 when the case or the matter is already linked. Requires a member role or above in the organization.","operationId":"LegalController_linkCaseToClioMatter","parameters":[{"name":"matterId","required":true,"in":"path","description":"matterId for the existing matter in CLIO","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkMatterDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"integrationType":{"type":"string","enum":["clio","smokeball"],"example":"clio"},"matterId":{"type":"string","example":"12345"},"matterName":{"type":"string","description":"The matter reference number, or its name when it has none.","example":"00123-Smith"},"attorneys":{"type":"array","items":{"type":"object","properties":{"contact":{"type":"object","properties":{"id":{"type":"string","example":"987654"},"name":{"type":"string","example":"Jane Doe"},"firstName":{"type":"string","example":"Jane"},"lastName":{"type":"string","example":"Doe"},"email":{"type":"string","example":"jane.doe@example.com"},"phone":{"type":"string","example":"+15555550123"},"type":{"type":"string","example":"Person"}}},"hearsayUserId":{"type":"string","description":"Present when the attorney has a user account.","example":"65a1b2c3d4e5f6a7b8c9d0e4"}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Link a case to a Clio matter","tags":["legal"]},"delete":{"description":"Removes the link between the case in `caseId` and the Clio matter, with its sync bookkeeping and imported participants; documents already synced and case access granted to attorneys stay. Only an org admin or owner or the case creator may unlink it (403 otherwise). Returns 400 without `caseId`, 404 when the case is not linked to Clio and 409 when it is linked to a different matter. Requires a member role or above in the organization.","operationId":"LegalController_unlinkCaseFromClioMatter","parameters":[{"name":"matterId","required":true,"in":"path","description":"matterId of the currently linked Clio matter","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"query","description":"Hearsay case to unlink from the matter","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"integrationType":{"type":"string","enum":["clio","smokeball"],"example":"clio"},"caseId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"matterId":{"type":"string","example":"12345"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Unlink a case from a Clio matter","tags":["legal"]}},"/legal/smokeball/matters":{"get":{"description":"Lists a page of matters from the connected Smokeball account, through the caller's own Smokeball connection when they have one and the firm's primary connection otherwise, filtered by `status` and `query` (25 per page by default, at most 200), each marked with whether it is already linked to a case. Requires a member role or above in the organization; returns 403 while the Smokeball integration is turned off.","operationId":"LegalController_getSmokeballMatters","parameters":[{"name":"status","required":false,"in":"query","schema":{"enum":["open","pending","closed"],"type":"string"}},{"name":"query","required":false,"in":"query","description":"Search term to filter matters by name/description","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page number (default 1)","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (default 25, max 200)","schema":{"type":"number"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LegalMattersResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Smokeball matters","tags":["legal"]}},"/legal/smokeball/matters/{matterId}/link":{"post":{"description":"Links the case in `caseId` to a Smokeball matter: grants the matter's attorneys access to the case, imports them as participants and starts the first document sync. Returns the matter id, its reference number (or name) and the attorneys. Only an org admin or owner or the case creator may link it, and not an opposing counsel organization (403 otherwise). Returns 404 for an unknown case or matter and 409 when the case or the matter is already linked. Requires a member role or above in the organization; returns 403 while the Smokeball integration is turned off.","operationId":"LegalController_linkCaseToSmokeballMatter","parameters":[{"name":"matterId","required":true,"in":"path","description":"matterId for the existing matter in Smokeball","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkMatterDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"integrationType":{"type":"string","enum":["clio","smokeball"],"example":"clio"},"matterId":{"type":"string","example":"12345"},"matterName":{"type":"string","description":"The matter reference number, or its name when it has none.","example":"00123-Smith"},"attorneys":{"type":"array","items":{"type":"object","properties":{"contact":{"type":"object","properties":{"id":{"type":"string","example":"987654"},"name":{"type":"string","example":"Jane Doe"},"firstName":{"type":"string","example":"Jane"},"lastName":{"type":"string","example":"Doe"},"email":{"type":"string","example":"jane.doe@example.com"},"phone":{"type":"string","example":"+15555550123"},"type":{"type":"string","example":"Person"}}},"hearsayUserId":{"type":"string","description":"Present when the attorney has a user account.","example":"65a1b2c3d4e5f6a7b8c9d0e4"}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Link a case to a Smokeball matter","tags":["legal"]},"delete":{"description":"Removes the link between the case in `caseId` and the Smokeball matter, with its sync bookkeeping and imported participants; documents already synced and case access granted to attorneys stay. Only an org admin or owner or the case creator may unlink it (403 otherwise). Returns 400 without `caseId`, 404 when the case is not linked to Smokeball and 409 when it is linked to a different matter. Requires a member role or above in the organization.","operationId":"LegalController_unlinkCaseFromSmokeballMatter","parameters":[{"name":"matterId","required":true,"in":"path","description":"matterId of the currently linked Smokeball matter","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"query","description":"Hearsay case to unlink from the matter","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"integrationType":{"type":"string","enum":["clio","smokeball"],"example":"clio"},"caseId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"matterId":{"type":"string","example":"12345"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Unlink a case from a Smokeball matter","tags":["legal"]}},"/legal/cases/{caseId}/participants":{"get":{"description":"Lists the people imported from the Clio or Smokeball matters linked to the case (attorneys and contacts) with name, email, phone, role, whether they are a custodian and their provider id; the optional `provider` query narrows to one provider. Empty when the case is not linked. Requires a member role or above in the organization.","operationId":"LegalController_getExternalParticipantsByCaseId","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"provider","required":true,"in":"query","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"participants":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e7"},"organizationId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"caseId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"caseIntegrationId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e8"},"provider":{"type":"string","example":"clio"},"firstName":{"type":"string","example":"Jane"},"lastName":{"type":"string","example":"Doe"},"email":{"type":"string","example":"jane.doe@example.com"},"phone":{"type":"string","example":"+15555550123"},"role":{"type":"string","enum":["client","witness","opposing_party","other","attorney"],"example":"attorney"},"isCustodian":{"type":"boolean","example":false},"externalId":{"type":"string","example":"987654"},"hearsayUserId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e4"},"extractionCodeId":{"type":"string"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List the matter participants of a case","tags":["legal"]}},"/legal/{provider}/settings":{"patch":{"description":"Updates the organization's sync settings for one destination; only the fields sent change. For Clio and Smokeball the webhook registration follows the change, and a webhook failure is logged without failing the save. Dropbox has outbound settings only, so sending `cases` or `documents` for it returns 400, as does an unknown provider. Requires an org admin or owner.","operationId":"LegalController_updateLegalSettings","parameters":[{"name":"provider","required":true,"in":"path","description":"Export destination: a CMP (clio, smokeball) or a storage backend (dropbox). Storage destinations are outbound-only — sending 'cases' or 'documents' for one is a 400.","schema":{"enum":["clio","smokeball","dropbox"],"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLegalSettingsDto"}}}},"responses":{"204":{"description":"Settings saved. No body."}},"security":[{"access-token":[]}],"summary":"Update the sync settings of a destination","tags":["legal"]},"get":{"description":"Returns the organization's sync settings for one destination: inbound case creation and document sync (Clio and Smokeball only) and outbound export per data source, or the defaults when it was never configured. An unknown provider returns 400. Requires a member role or above in the organization.","operationId":"LegalController_getLegalSettings","parameters":[{"name":"provider","required":true,"in":"path","description":"Export destination: a CMP (clio, smokeball) or a storage backend (dropbox).","schema":{"enum":["clio","smokeball","dropbox"],"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"Sync settings for the destination.","properties":{"inbound":{"type":"object","description":"Clio and Smokeball only.","properties":{"cases":{"type":"object","properties":{"autoCreateFromWebhook":{"type":"boolean","example":false},"autoCollection":{"type":"object"}}},"documents":{"type":"object","properties":{"autoSync":{"type":"boolean","example":false}}}}},"outbound":{"type":"object","properties":{"dataSources":{"type":"object","description":"Per data source: enabled, mode (auto or manual) and source options.","example":{"file":{"enabled":false,"mode":"auto"},"email":{"enabled":false,"mode":"manual"}}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the sync settings of a destination","tags":["legal"]}},"/legal/cases/{caseId}/legal-sync/export-to-provider":{"post":{"description":"Queues an export of the case data not yet sent to the destination in `provider` (Clio by default, Smokeball or Dropbox), narrowed by source types, extraction codes and formats, and returns what the request matched: `queued`, the job id and the record counts per source. A request while an export of the same case and destination is running joins it. `queued: false` means nothing matched and no job started; delivery status per record is on the legal sync status route. Returns 409 when a requested source is turned off for export or matches nothing and 404 for an unknown case. Requires a member role or above in the organization and access to the case; not available to opposing counsel.","operationId":"LegalSyncController_uploadPendingSourceDataToProvider","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportToProviderDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExportToProviderResultEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}},"409":{"description":"An explicitly requested source is disabled for outbound export, or matched no exportable records."}},"security":[{"access-token":[]}],"summary":"Export case data to a provider","tags":["legal"]}},"/legal/cases/{caseId}/legal-sync/status":{"get":{"description":"Lists what has been or is being sent from the case to Clio, Smokeball or Dropbox, one row per record and destination, most recently updated first, with status, file name, target matter or folder, error and timings; filter by `provider`, `status` and `sourceType`. Destinations whose integration is turned off are left out. Requires a member role or above in the organization.","operationId":"LegalSyncController_getCaseSyncStatus","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"provider","required":false,"in":"query","schema":{"type":"string","enum":["clio","smokeball","dropbox"]}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["pending","processing","completed","failed","disabled","skipped"]}},{"name":"sourceType","required":false,"in":"query","schema":{"type":"string","enum":["Device","UserEmailDataProvider","CaseFileProvider","FinancialAccountProvider","LegalDocumentProvider","DiscordUserProvider","RedditUserProvider","LinkedInUserProvider","AiChatProvider"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"records":{"type":"array","items":{"$ref":"#/components/schemas/LegalSyncRecordEntity"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List the legal sync records of a case","tags":["legal"]}},"/legal/clio/matters/{matterId}/billing/expense":{"post":{"description":"Test route: creates a billable expense (`amount`, `description`, an optional `date` that defaults to today and an optional expense category) directly on a Clio matter by its Clio id, through the firm's primary connection, and returns the created entry in `activity`. Requires an org admin or owner; returns 403 while the Clio integration is turned off.","operationId":"LegalBillingController_createClioExpenseEntry","parameters":[{"name":"matterId","required":true,"in":"path","description":"Clio matter ID to attach the expense to","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLegalExpenseDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"activity":{"type":"object","properties":{"id":{"type":"string","example":"445566"},"matterId":{"type":"string","example":"12345"},"date":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"},"description":{"type":"string","example":"Hearsay: phone extraction upload"},"type":{"type":"string","enum":["time","flat_fee","expense"],"example":"expense"},"rate":{"type":"number","example":49},"total":{"type":"number","example":49},"staffId":{"type":"string"}}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a Clio expense entry (internal)","tags":["legal"],"x-internal":true}},"/legal/smokeball/matters/{matterId}/billing/expense":{"post":{"description":"Test route: creates a billable expense (`amount`, `description`, an optional `date` that defaults to today and an optional expense category) directly on a Smokeball matter by its Smokeball id, through the firm's primary connection, and returns the created entry in `activity`. Requires an org admin or owner; returns 403 while the Smokeball integration is turned off.","operationId":"LegalBillingController_createSmokeballExpenseEntry","parameters":[{"name":"matterId","required":true,"in":"path","description":"Smokeball matter ID to attach the expense to","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLegalExpenseDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"activity":{"type":"object","properties":{"id":{"type":"string","example":"445566"},"matterId":{"type":"string","example":"12345"},"date":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"},"description":{"type":"string","example":"Hearsay: phone extraction upload"},"type":{"type":"string","enum":["time","flat_fee","expense"],"example":"expense"},"rate":{"type":"number","example":49},"total":{"type":"number","example":49},"staffId":{"type":"string"}}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a Smokeball expense entry (internal)","tags":["legal"],"x-internal":true}},"/legal/clio/connect":{"get":{"description":"Returns the Clio authorization URL to send the user to, for the firm's Clio `region` (us, ca, eu or au; us by default). The first user to connect becomes the firm's primary connection. Returns 409 when the caller already has an active Clio connection in this organization. Requires a member role or above in the organization.","operationId":"LegalConnectionController_connectClio","parameters":[{"name":"region","required":false,"in":"query","description":"The firm's Clio region (defaults to 'us')","schema":{"enum":["us","ca","eu","au"],"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"authorizationUrl":{"type":"string","example":"https://app.clio.com/oauth/authorize?response_type=code&client_id=abc123&state=9f2c1e"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a Clio connection","tags":["legal"]}},"/legal/clio/callback":{"get":{"description":"Where Clio sends the browser back after the user authorizes; it needs no sign-in, the `state` value ties it to the connect request. Saves the connection and redirects (302) to the portal with `clio=connected`. Returns 401 when `code` or `state` is missing.","operationId":"LegalConnectionController_clioCallback","parameters":[{"name":"code","required":true,"in":"query","schema":{"type":"string"}},{"name":"state","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"302":{"description":"Redirects (Location header) to the portal with `clio=connected`."}},"security":[{"access-token":[]}],"summary":"Finish a Clio connection (internal)","tags":["legal"],"x-internal":true}},"/legal/clio/firm":{"delete":{"description":"Disconnects Clio for the whole organization: turns off every Clio sync setting, removes the Clio webhooks, deactivates the firm and every user connection, and unlinks every case from its Clio matter (synced documents and case access stay). Requires an org admin or owner.","operationId":"LegalConnectionController_disconnectClio","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Disconnected. No body."}},"security":[{"access-token":[]}],"summary":"Disconnect Clio for the organization","tags":["legal"]}},"/legal/clio/connections/me":{"delete":{"description":"Disconnects the caller's own Clio connection. When it was the firm's primary connection, another connected user becomes primary, or the firm is disconnected when there is none. Returns 404 when the caller has no active Clio connection. Requires a member role or above in the organization.","operationId":"LegalConnectionController_disconnectMyClio","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Disconnected. No body."}},"security":[{"access-token":[]}],"summary":"Disconnect my Clio connection","tags":["legal"]}},"/legal/smokeball/connect":{"get":{"description":"Returns the Smokeball authorization URL to send the user to, for the firm's Smokeball `region` (us, au or uk; us by default). The first user to connect becomes the firm's primary connection. Returns 409 when the caller already has an active Smokeball connection in this organization and 403 while the Smokeball integration is turned off. Requires a member role or above in the organization.","operationId":"LegalConnectionController_connectSmokeball","parameters":[{"name":"region","required":false,"in":"query","description":"The firm's Smokeball region (defaults to 'us')","schema":{"enum":["us","au","uk"],"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"authorizationUrl":{"type":"string","example":"https://auth.smokeball.com/oauth2/authorize?response_type=code&client_id=abc123&state=9f2c1e"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a Smokeball connection","tags":["legal"]}},"/legal/smokeball/callback":{"get":{"description":"Where Smokeball sends the browser back after the user authorizes; it needs no sign-in, the `state` value ties it to the connect request. Saves the connection and redirects (302) to the portal with `smokeball=connected`. Returns 401 when `code` or `state` is missing and 403 while the Smokeball integration is turned off.","operationId":"LegalConnectionController_smokeballCallback","parameters":[{"name":"code","required":true,"in":"query","schema":{"type":"string"}},{"name":"state","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"302":{"description":"Redirects (Location header) to the portal with `smokeball=connected`."}},"security":[{"access-token":[]}],"summary":"Finish a Smokeball connection (internal)","tags":["legal"],"x-internal":true}},"/legal/smokeball/firm":{"delete":{"description":"Disconnects Smokeball for the whole organization: turns off every Smokeball sync setting, removes the Smokeball webhooks, deactivates the firm and every user connection, and unlinks every case from its Smokeball matter (synced documents and case access stay). Requires an org admin or owner.","operationId":"LegalConnectionController_disconnectSmokeball","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Disconnected. No body."}},"security":[{"access-token":[]}],"summary":"Disconnect Smokeball for the organization","tags":["legal"]}},"/legal/smokeball/connections/me":{"delete":{"description":"Disconnects the caller's own Smokeball connection. When it was the firm's primary connection, another connected user becomes primary, or the firm is disconnected when there is none. Returns 404 when the caller has no active Smokeball connection. Requires a member role or above in the organization.","operationId":"LegalConnectionController_disconnectMySmokeball","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Disconnected. No body."}},"security":[{"access-token":[]}],"summary":"Disconnect my Smokeball connection","tags":["legal"]}},"/legal/dropbox/connect":{"get":{"description":"Returns the Dropbox authorization URL to send the user to; the connection covers the whole organization. Requires an org admin or owner; returns 403 while the Dropbox integration is turned off.","operationId":"DropboxConnectionController_connectDropbox","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"authorizationUrl":{"type":"string","example":"https://www.dropbox.com/oauth2/authorize?response_type=code&client_id=abc123&state=9f2c1e"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a Dropbox connection","tags":["legal"]}},"/legal/dropbox/callback":{"get":{"description":"Where Dropbox sends the browser back after the user authorizes; it needs no sign-in, the `state` value ties it to the connect request. Saves the connection and redirects (302) to the portal with `dropbox=connected`. Returns 401 when `code` or `state` is missing and 403 while the Dropbox integration is turned off.","operationId":"DropboxConnectionController_dropboxCallback","parameters":[{"name":"code","required":true,"in":"query","schema":{"type":"string"}},{"name":"state","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"302":{"description":"Redirects (Location header) to the portal with `dropbox=connected`."}},"security":[{"access-token":[]}],"summary":"Finish a Dropbox connection (internal)","tags":["legal"],"x-internal":true}},"/legal/dropbox/status":{"get":{"description":"Returns whether the Dropbox integration is turned on (`enabled`) and whether the organization has an active Dropbox connection, with the Dropbox account id and when it was connected. `enabled` can be false for an organization that is still connected. Requires a member role or above in the organization.","operationId":"DropboxConnectionController_getDropboxStatus","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"enabled":{"type":"boolean","example":true},"connected":{"type":"boolean","example":true},"remoteAccountId":{"type":"string","nullable":true,"description":"Present when connected.","example":"dbid:AAH4f99T0taONIb-OurWxbNQ6ywGRopQngc"},"connectedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","description":"Present when connected."}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the Dropbox connection status","tags":["legal"]}},"/legal/dropbox/connection":{"delete":{"description":"Deactivates the organization's Dropbox connection. The folders chosen for cases are kept, so reconnecting the same account needs no new choices. Works while the integration is turned off. Returns 404 when there is no active connection. Requires an org admin or owner.","operationId":"DropboxConnectionController_disconnectDropbox","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Disconnected. No body."}},"security":[{"access-token":[]}],"summary":"Disconnect Dropbox","tags":["legal"]}},"/legal/dropbox/folders":{"get":{"description":"Lists the folders one level below `path` (the account root when omitted) in the connected Dropbox, each with the `path` to pass back to go one level deeper. Requires a member role or above in the organization; returns 403 while the Dropbox integration is turned off.","operationId":"DropboxConnectionController_listDropboxFolders","parameters":[{"name":"path","required":false,"in":"query","description":"Folder to list. Omit for the account root. Use a `path` returned by a previous call.","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"folders":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","example":"/Clients/Smith v. Acme"},"name":{"type":"string","example":"Smith v. Acme"}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Dropbox folders","tags":["legal"]}},"/legal/cases/{caseId}/dropbox/folder":{"get":{"description":"Returns the Dropbox folder the case's exports go to. `path: null` means no folder has been chosen, and an export to Dropbox is refused until one is. Requires a member role or above in the organization and access to the case.","operationId":"DropboxConnectionController_getCaseDropboxFolder","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"path":{"type":"string","nullable":true,"example":"/Clients/Smith v. Acme"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the Dropbox folder of a case","tags":["legal"]},"put":{"description":"Sets the Dropbox folder the case's later exports go to; documents already sent stay where they are. Returns 404 when `path` is not an existing folder in the connected account. Requires a member role or above in the organization and access to the case; not available to opposing counsel; returns 403 while the Dropbox integration is turned off.","operationId":"DropboxConnectionController_setCaseDropboxFolder","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCaseDestinationFolderDto"}}}},"responses":{"204":{"description":"Folder saved. No body."}},"security":[{"access-token":[]}],"summary":"Set the Dropbox folder of a case","tags":["legal"]},"delete":{"description":"Removes the case's Dropbox folder, so exports to Dropbox are refused until a new one is chosen. Works while the integration is turned off. Requires a member role or above in the organization and access to the case; not available to opposing counsel.","operationId":"DropboxConnectionController_clearCaseDropboxFolder","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Folder cleared. No body."}},"security":[{"access-token":[]}],"summary":"Clear the Dropbox folder of a case","tags":["legal"]}},"/legal/clio/webhook/matters":{"post":{"description":"Called by Clio when a matter is created, updated or deleted. Deliveries are signed with the subscription secret and rejected with 401 when the signature is missing or does not match. The registration handshake, which carries an `X-Hook-Secret` header, is answered by echoing that header. Processing errors are logged and still answered 200, so Clio keeps the subscription.","operationId":"LegalClioWebhookController_clioMatterWebhook","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClioMatterWebHookDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["statusCode","timestamp"],"properties":{"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"summary":"Receive Clio matter events (internal)","tags":["legal"],"x-internal":true}},"/legal/clio/webhook/documents":{"post":{"description":"Called by Clio when a document is created, updated or deleted. Deliveries are signed with the subscription secret and rejected with 401 when the signature is missing or does not match. The registration handshake, which carries an `X-Hook-Secret` header, is answered by echoing that header. Processing errors are logged and still answered 200, so Clio keeps the subscription.","operationId":"LegalClioWebhookController_clioDocumentWebhook","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClioDocumentWebhookDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["statusCode","timestamp"],"properties":{"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"summary":"Receive Clio document events (internal)","tags":["legal"],"x-internal":true}},"/legal/clio/deauthorize":{"post":{"description":"Called by Clio when a user removes access to the app from Clio. Deactivates that user's Clio connection (`user_id`); when it was the firm's primary connection, another connected user becomes primary, or the firm is disconnected when there is none. Answers 200 with an empty body, also when `user_id` is missing or unknown.","operationId":"LegalClioWebhookController_clioDeauthorize","parameters":[],"responses":{"200":{"description":"Empty body (not wrapped)."}},"summary":"Receive a Clio deauthorization (internal)","tags":["legal"],"x-internal":true}},"/legal/smokeball/webhook":{"post":{"description":"Called by Smokeball for matter and file events on the firm's single subscription. Requests must carry a valid signature, timestamp and request id (401 otherwise). Answers 200 without processing while the Smokeball integration is turned off, for changes Hearsay itself made and for duplicate events.","operationId":"LegalSmokeballWebhookController_smokeballWebhook","parameters":[{"name":"requestid","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmokeballWebhookDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["statusCode","timestamp"],"properties":{"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"summary":"Receive Smokeball events (internal)","tags":["legal"],"x-internal":true}},"/legal/status":{"get":{"description":"Lists one entry per legal provider (Clio, Smokeball) the organization has connected: its status (`degraded` when no user connection is primary), the provider account id, when it was connected, how many users are connected, the primary user connection and whether the matter and document webhooks are registered. An empty list means none is connected. Requires a member role or above in the organization.","operationId":"LegalConnectionStatusController_getConnectionStatuses","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"connections":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","enum":["clio","smokeball"],"example":"clio"},"status":{"type":"string","enum":["connected","not_connected","degraded"],"example":"connected"},"remoteAccountId":{"type":"string","example":"3456789"},"connectedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"},"activeUserCount":{"type":"number","example":2},"primaryUser":{"type":"object","description":"Absent when status is degraded.","properties":{"hearsayUserId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e4"},"remoteUserId":{"type":"string","example":"345678901"},"tokenExpiresAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"},"lastUsedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}},"webhooks":{"type":"object","properties":{"matters":{"type":"boolean","example":true},"documents":{"type":"boolean","example":true}}}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List legal provider connections","tags":["legal"]}},"/admin/case":{"get":{"description":"Lists cases across all organizations, most recently updated first, leaving out deleted cases, narrowed by `orgIds` and by `searchText` (matched against name, description and slug). Each row carries the full case details. Paged with `page` and `perPage` (default 10). Super admin only.","operationId":"CaseAdminController_searchCases","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"orgIds","required":false,"in":"query","description":"Comma-separated organization ids to filter cases by","schema":{"type":"array","items":{"type":"string"}}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":1,"maxLength":100,"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseDetailsEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search all cases (internal)","tags":["admin"],"x-internal":true}},"/admin/case/{caseId}/events":{"get":{"description":"Lists every event recorded on the case, newest first, in every category, with actors' names and emails shown in full, filtered by `type` and `category` and paged with `page` and `perPage`. Super admin only.","operationId":"CaseAdminController_listCaseEvents","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"maximum":100,"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"type","required":false,"in":"query","schema":{"type":"string","enum":["financial_connection_removed","oc_invited","oc_accepted","oc_member_added","oc_member_removed","oc_ec_created","oc_suggestion_created","oc_suggestion_updated","oc_suggestion_message","oc_suggestion_rejected","oc_ec_approved","oc_data_approved","oc_data_revoked","oc_code_released","oc_code_unreleased","member_invited","member_accepted","case_created","case_status_changed","extraction_code_created","extraction_code_updated","extraction_code_locked","custodian_updated","device_attached","data_uploaded","export_started","export_finished","export_errored","export_cancelled"]}},{"name":"category","required":false,"in":"query","schema":{"type":"string","enum":["general","case_data","oc"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseEventEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a case's event log (internal)","tags":["admin"],"x-internal":true}},"/admin/case/create-case":{"post":{"description":"Creates a case in `organization` on behalf of the user given by `userId` or `userEmail`, who becomes its owner, with the same options as the member route that creates a case, and returns the case. Returns 404 for an unknown user and 400 when the organization already has a case with that name. Super admin only.","operationId":"CaseAdminController_createCase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCaseForUserDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseViewEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a case for a user (internal)","tags":["admin"],"x-internal":true}},"/admin/case/delete-case-conversations":{"delete":{"description":"Removes every conversation from the case found by `caseId` or `caseSlug` in the organization given by `orgId` or `orgSlug`: conversations linked only to this case are deleted and the rest are unlinked. With `deleteCaseIfNoConversationsLeft`, a case left with no conversations is soft-deleted along with its access, extraction codes, comments, data sources and files. Returns a message with the counts, or 404 for an unknown organization or case. Super admin only.","operationId":"CaseAdminController_deleteCaseConversations","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteCaseConversationsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a case's conversations (internal)","tags":["admin"],"x-internal":true}},"/admin/case/create-extraction-codes":{"post":{"description":"Creates one extraction code for each entry in `ecCodeData`, on its `caseId` with the given custodian email, configuration and requested sources, marks those sources pending on the case and returns each case id with its new code. While the Plaid integration is on, a financial source needs the case's organization to have accepted the Plaid terms and have an active Plaid customer (400 or 404 otherwise); this check runs after the codes are saved. Super admin only.","operationId":"CaseAdminController_createExtractionCodes","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMultipleExtractionCodeDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CreateExtractionCodeResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create extraction codes for cases (internal)","tags":["admin"],"x-internal":true}},"/admin/case/update-extraction-codes":{"post":{"description":"Updates the extraction code on `caseId` found by `extractionCodeId` or `code`, with the same fields as the member route (label, due date, configuration or custodian), and returns the code. Configuration cannot change once the code is locked. Returns 404 when the case has no such code. Super admin only.","operationId":"CaseAdminController_updateExtractionCodes","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateExtractionCodeAdminDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update an extraction code (internal)","tags":["admin"],"x-internal":true}},"/admin/case/fetch-extraction-codes":{"get":{"description":"Lists extraction codes newest first, narrowed by `caseId` or `orgId`, each with its case, paged with `page` and `perPage`. Super admin only.","operationId":"CaseAdminController_fetchExtractionCodes","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"caseId","required":false,"in":"query","schema":{"type":"string"}},{"name":"orgId","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeEntity"}}}}]},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List extraction codes (internal)","tags":["admin"],"x-internal":true}},"/admin/case/create-bulk-extraction-codes":{"post":{"description":"Creates an extraction code for every case that has none, or for every case when `createIfEvenExists` is true, in batches of 1000, with the case creator as custodian and default settings. Returns a message with the number created. Super admin only.","operationId":"CaseAdminController_createBulkExtractionCode","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBulkExtractionCodeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create extraction codes for all cases (internal)","tags":["admin"],"x-internal":true}},"/admin/case/rerun-export":{"post":{"description":"Clears the export's completion and progress and queues it to run again with the same settings, even when it already completed. Returns 404 for an unknown export and 400 for a collection export, which is no longer supported. Super admin only.","operationId":"CaseAdminController_rerunExport","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RerunExportDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Re-run an export (internal)","tags":["admin"],"x-internal":true}},"/admin/case/rerun-exports":{"post":{"description":"Creates a batch from `exportIds` and queues it; the batch re-runs the exports one at a time, waiting for each to finish. Ids that match no export are counted as invalid. Returns the batch id, the number of ids and the number invalid; follow progress with the batch route. Super admin only.","operationId":"CaseAdminController_rerunExports","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RerunExportsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RerunExportsResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Re-run several exports as a batch (internal)","tags":["admin"],"x-internal":true}},"/admin/case/rerun-exports/{batchId}":{"get":{"description":"Returns the batch's status, its counts of exports that succeeded and failed, its timings and each export's status and error. Returns 404 for an unknown batch. Super admin only.","operationId":"CaseAdminController_getRerunBatch","parameters":[{"name":"batchId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseExportRerunBatchEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}},"404":{"description":"Rerun batch not found"}},"security":[{"access-token":[]}],"summary":"Get a re-run batch (internal)","tags":["admin"],"x-internal":true}},"/admin/case/clear-export-locks":{"post":{"description":"Deletes every export lock, including ones left by workers that crashed or restarted, so blocked exports can start again, and returns how many were cleared. Super admin only.","operationId":"CaseAdminController_clearExportLocks","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Clear all export locks (internal)","tags":["admin"],"x-internal":true}},"/admin/case/fetch-messages-count":{"get":{"description":"Counts the conversations linked to the case, their messages and their export copies of messages. Returns 404 for an unknown case. Super admin only.","operationId":"CaseAdminController_fetchMessagesCount","parameters":[{"name":"caseId","required":true,"in":"query","description":"The case ID","schema":{"example":"683cce0e314514073ab59156","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/FetchMessagesCountResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Count a case's messages (internal)","tags":["admin"],"x-internal":true}},"/admin/case/move-case-to-org":{"post":{"description":"Moves the case to `targetOrgId`: its devices are added to that organization, all its case access is replaced with admin access for the users in `emails` (each must already belong to the target organization) and its extraction codes are reassigned to the first of them. Returns 404 for an unknown case, organization or users, and 400 when the case is already there or some emails have no user or no membership there. Super admin only.","operationId":"CaseAdminController_moveCaseToOrg","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveCaseToOrgDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Move a case to another organization (internal)","tags":["admin"],"x-internal":true}},"/admin/case/move-device-to-case":{"post":{"description":"Moves a device and what was collected from it (extraction codes, backups, conversations, contacts, media, browser history, notes, files, comments and data source statuses) from `sourceCaseId` to `targetCaseId` in the same organization; with `dryRun` it only reports what would move. The message lists the counts and any step that failed. Returns 404 for an unknown case or device and 400 when the cases are the same, in different organizations, or the device is not on the source case. Super admin only.","operationId":"CaseAdminController_moveDeviceToCase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveDeviceToCaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Move a device to another case (internal)","tags":["admin"],"x-internal":true}},"/admin/case/delete-discord-profile":{"delete":{"description":"Archives the Discord profile in `discordProviderId`, or with `forceDelete` deletes it with all its conversations, messages, activity, support tickets and import jobs; either way the data source status of its linked cases is reset. Returns 404 for an unknown profile. Super admin only.","operationId":"CaseAdminController_deleteDiscordProfile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminDeleteDiscordProfileDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a Discord profile (internal)","tags":["admin"],"x-internal":true}},"/admin/case/delete-reddit-profile":{"delete":{"description":"Archives the Reddit profile in `redditProviderId`, or with `forceDelete` deletes it with all its posts, comments, messages, chat messages, votes and import jobs; either way the data source status of its linked cases is reset. Returns 404 for an unknown profile. Super admin only.","operationId":"CaseAdminController_deleteRedditProfile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminDeleteRedditProfileDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a Reddit profile (internal)","tags":["admin"],"x-internal":true}},"/admin/organization":{"get":{"description":"Lists organizations whose name, slug or email contains `searchText` (case-insensitive), one page at a time, sorted by `sortBy` (newest first by default), each with its active plan. Super admin only.","operationId":"OrganizationAdminController_searchOrganizations","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","schema":{"minLength":1,"maxLength":100,"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"createdAt","type":"string","enum":["createdAt","updatedAt","name"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search organizations (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/directory":{"get":{"description":"Lists organizations one page at a time (newest first by default), each with a capped list of its members (id, name, email, role in that organization, active state) that leaves out claimable placeholder accounts and deleted users; `membershipCount` counts every membership and `hasMoreUsers` means the list was cut, so page the rest with `GET /admin/user?organizationId=<id>` using `skip`/`limit`. `searchText` matches the organization name, slug or email, not users, and `total` counts organizations. Super admin only.","operationId":"OrganizationAdminController_getOrganizationDirectory","parameters":[{"name":"page","required":false,"in":"query","schema":{"maximum":1000,"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"maximum":100,"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"Takes precedence over page/perPage when provided. Mutually exclusive with page in practice: nextPage/previousPage track `page`, so they are meaningless when paging by skip.","schema":{"maximum":100000,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Takes precedence over perPage when provided.","schema":{"maximum":100,"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Case-insensitive partial match on organization name, slug, or email. Does not match users.","schema":{"minLength":1,"maxLength":100,"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"createdAt","type":"string","enum":["createdAt","updatedAt","name"]}},{"name":"isActive","required":false,"in":"query","description":"Filter by the ORGANIZATION's active state. Does not filter the nested users.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationDirectoryEntryEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List organizations with their members (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/slug/{slug}":{"get":{"description":"Returns the organization with that slug and its billing summary (plan, limits and prices, or null on the legacy model); `permission` is always null here. Returns 404 for an unknown slug. Super admin only.","operationId":"OrganizationAdminController_getOrganizationBySlug","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationDetailsEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization by slug (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/id/{orgId}":{"get":{"description":"Returns the organization and its billing summary (plan, limits and prices, or null on the legacy model); `permission` is always null here. Returns 400 for a malformed id and 404 for an unknown one. Super admin only.","operationId":"OrganizationAdminController_getOrganizationById","parameters":[{"name":"orgId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationDetailsEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization by id (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/storage-stats":{"get":{"description":"Lists organizations by stored data, largest first, with each case's name, creation date and size in bytes summed from its collection records, optionally only for `organizationIds` (comma-separated, up to 1000); one page at a time. Super admin only.","operationId":"OrganizationAdminController_getOrganizationStorageStats","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"organizationIds","required":false,"in":"query","description":"Filter by organization ids","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationStorageStatsItemEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get storage use per organization (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/add-all-users-to-all-org-cases":{"post":{"description":"Grants every member of organization `orgId` access at `caseAccessLevel` to every case of the organization; access a member already has is left unchanged. Returns `isSuccess: false` when some grants failed, 400 when the organization has no members or no cases, and 404 for an unknown organization. Super admin only.","operationId":"OrganizationAdminController_addAllUsersToAllOrgCases","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAllUsersToAllOrgCasesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Give every member access to every case (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/add-user-to-all-org-cases":{"post":{"description":"Grants the member with `email` access at `caseAccessLevel` to every active case of organization `orgId` they cannot reach yet; archived and deleted cases are skipped and existing access, including revoked access, is kept as is. Returns `isSuccess: false` when processing failed part way (retry to fill the rest), 400 when the user is not a member or there are no active cases, and 404 for an unknown organization or user or while the feature is turned off. Super admin only.","operationId":"OrganizationAdminController_addUserToAllOrgCases","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUserToAllOrgCasesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}},"400":{"description":"Invalid input, user is not an organization member, or no active cases found."},"404":{"description":"Feature disabled, organization not found, or user not found."}},"security":[{"access-token":[]}],"summary":"Give one member access to every active case (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/add-user-to-org":{"post":{"description":"Adds the user with `email` to the organization given by `orgId` or `orgName` with role `invitedUserRole` (member by default), first creating the account with `name` and `password` (random when omitted) when no user has that email, and recalculates the seats. Sends a password-reset email unless `sendResetPasswordEmail` is false, and succeeds without changes when the user is already a member; returns 404 for an unknown organization. Super admin only.","operationId":"OrganizationAdminController_addUserToOrg","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddNewUserToOrgDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Add a user to an organization (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/remove-user-from-org":{"post":{"description":"Removes the membership of the user given by `userId` or `email` from the organization given by `orgId` or `orgName`; the user account itself is kept. Returns 404 for an unknown organization or user and 400 when the user is not a member. Super admin only.","operationId":"OrganizationAdminController_removeUserFromOrg","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveUserFromOrgDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove a user from an organization (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/transfer-ownership/{orgId}":{"patch":{"description":"Makes the member `newOwnerEmail` the owner of the organization and its contact email, and turns `currentOwnerEmail` into an admin. Returns 400 when both emails are the same, the current user is not the owner or the new one is not a member, and 404 for an unknown organization or user. Super admin only.","operationId":"OrganizationAdminController_transferOwnership","parameters":[{"name":"orgId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransferOrgOwnershipDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Transfer organization ownership (internal)","tags":["admin"],"x-internal":true}},"/admin/organization/update-org/{orgId}":{"patch":{"description":"Updates the profile fields that are sent (name, email, logo, website, contact, address, description, phone, version); `metadata` is merged into the existing metadata instead of replacing it. Returns the updated organization, or 404 for an unknown one. Super admin only.","operationId":"OrganizationAdminController_updateOrg","parameters":[{"name":"orgId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminOrganizationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update an organization (internal)","tags":["admin"],"x-internal":true}},"/admin/common/cache/reset":{"post":{"description":"Clears every entry in the shared cache; cached data is rebuilt from the database on next use. Super admin only.","operationId":"CommonAdminController_deleteAllCache","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Clear the whole cache (internal)","tags":["admin"],"x-internal":true}},"/admin/common/cache/keys":{"get":{"description":"Returns the names of the cache keys matching the glob `pattern`, or every key when it is omitted. The lookup scans the whole key space, so prefer a narrow pattern. Super admin only.","operationId":"CommonAdminController_getCacheKeys","parameters":[{"name":"pattern","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"type":"string"},"example":["cache-master-data-source-all","cache-master-data-source-name-whatsapp"]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List cache keys (internal)","tags":["admin"],"x-internal":true}},"/admin/common/cache/value/{key}":{"get":{"description":"Returns the value stored under `key` as it was stored, of any JSON type; `data` is empty when the key is missing or has expired. Super admin only.","operationId":"CommonAdminController_getCacheValue","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"description":"The stored value, of any JSON type.","nullable":true,"example":{"name":"whatsapp","displayName":"WhatsApp"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a cache value (internal)","tags":["admin"],"x-internal":true},"post":{"description":"Stores `value` under `key`, replacing any existing value, for `ttl` milliseconds (at least 1000; one hour when omitted). Super admin only.","operationId":"CommonAdminController_setCacheValueForKey","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCacheValueDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Set a cache value (internal)","tags":["admin"],"x-internal":true},"delete":{"description":"Removes the entry stored under `key`; succeeds whether or not the key existed. Super admin only.","operationId":"CommonAdminController_deleteCacheByKey","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a cache key (internal)","tags":["admin"],"x-internal":true}},"/admin/user":{"get":{"description":"Lists the members of the organizations matched by `organizationId` or `organizationName` (one of the two is required; without either, no users are returned), optionally narrowed by `email` and `name` (partial, case-insensitive), with each user's full profile, one page at a time (10 users by default). Returns 404 when no user matches. Super admin only.","operationId":"UserAdminController_fetchUsers","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"email","required":true,"in":"query","schema":{"type":"string"}},{"name":"name","required":true,"in":"query","schema":{"type":"string"}},{"name":"organizationName","required":true,"in":"query","schema":{"type":"string"}},{"name":"organizationId","required":true,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"createdAt","type":"string","enum":["createdAt","updatedAt","name","email"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search users (internal)","tags":["admin"],"x-internal":true}},"/admin/user/add":{"post":{"description":"Creates a user account and its own organization as a sign-up would, optionally already email-verified or claimable, and returns the new account's access token, profile and organization. Super admin only.","operationId":"UserAdminController_addNewUser","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUserDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RegisterResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a user (internal)","tags":["admin"],"x-internal":true}},"/admin/user/set-password":{"post":{"description":"Sets the password of the user given by `userId` or `email`. Returns 404 for an unknown user. Super admin only.","operationId":"UserAdminController_setPassword","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetUserPasswordDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Set a user's password (internal)","tags":["admin"],"x-internal":true}},"/admin/user/send-reset-password-email":{"post":{"description":"Emails a password-reset link to the user given by `userId` or `email`. Returns 404 for an unknown user. Super admin only.","operationId":"UserAdminController_sendPasswordResetEmail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendPasswordResetEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Send a password-reset email (internal)","tags":["admin"],"x-internal":true}},"/admin/user/login-as":{"post":{"description":"Signs in as the user with `email` and returns their access and refresh tokens and profile, as a normal login would. Returns 404 for an unknown user and 400 when the account is not a full account or is inactive. Super admin only.","operationId":"UserAdminController_loginAs","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginAsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LoginResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Log in as a user (internal)","tags":["admin"],"x-internal":true}},"/admin/user/delete-account":{"post":{"description":"Permanently deletes the user with `email`, their memberships and every organization they own, with its cases, conversations, messages, collections and case access; this cannot be undone. Returns 404 for an unknown user. Super admin only.","operationId":"UserAdminController_deleteUserAccount","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteUserAccountDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a user account (internal)","tags":["admin"],"x-internal":true}},"/admin/user/update-email":{"post":{"description":"Changes the email of the user from `oldEmail` to `newEmail` (lower-cased), also updating the contact email of organizations they own that used the old address. Returns 400 when the two are equal or the new one is taken, and 404 when no user has the old one. Super admin only.","operationId":"UserAdminController_updateEmail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Change a user's email (internal)","tags":["admin"],"x-internal":true}},"/admin/user/lock":{"post":{"description":"Deactivates the user with `email` and the organizations they own, and ends all their sessions at once. Returns 404 for an unknown user. Super admin only.","operationId":"UserAdminController_lockUserByEmail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LockUserByEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Lock a user (internal)","tags":["admin"],"x-internal":true}},"/admin/user/unlock":{"post":{"description":"Reactivates the user with `email` and the organizations they own; sessions ended by the lock stay ended. Returns 404 for an unknown user. Super admin only.","operationId":"UserAdminController_unlockUserByEmail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlockUserByEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Unlock a user (internal)","tags":["admin"],"x-internal":true}},"/admin/report/device-uploads":{"post":{"description":"Counts devices with a completed backup that shared conversations, created between `startDate` and `endDate` (both optional, whole days), split into Android, iOS and other, plus the organizations whose cases hold them with their email, device count and plan type. Returns `{ isSuccess, message }` instead when no backup matches. Super admin only.","operationId":"ReportAdminController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FetchDeviceUploadReportDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"totalBackups":{"type":"number","example":12},"android":{"type":"number","example":5},"ios":{"type":"number","example":6},"others":{"type":"number","example":1},"organizations":{"type":"array","items":{"type":"object","properties":{"organization":{"type":"string","nullable":false,"example":"Example Firm"},"email":{"type":"string","nullable":false,"example":"admin@example.com"},"deviceCount":{"type":"number","example":3},"plan":{"type":"string","nullable":false,"example":"tier6"}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Count device uploads (internal)","tags":["admin"],"x-internal":true}},"/admin/report/latest-stats":{"get":{"description":"Returns platform-wide totals: shared messages, devices and organizations, plus message, WhatsApp, voicemail, call-log and contact counts summed over one successful extraction per device. Super admin only.","operationId":"ReportAdminController_fetchLatestStats","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/StatCountResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get platform totals (internal)","tags":["admin"],"x-internal":true}},"/admin/report/new-devices":{"post":{"description":"Lists devices whose first successful extraction falls in the month of `date` (through the month of `endDate` when sent), optionally only for organization `orgId`, with the organization's name, email, billing customer id and plan, the device type, the case name and whether conversations were uploaded. Super admin only.","operationId":"ReportAdminController_fetchNewDevices","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FetchNewDevicesReportDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/NewExtractionsReportResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List newly extracted devices (internal)","tags":["admin"],"x-internal":true}},"/admin/report/org-report":{"get":{"description":"Lists organizations whose name or email contains `searchText`, one page at a time, each with its active plan and an activity summary: account age, case and extraction-code counts, unused codes on recent cases, every extraction attempt with its outcome and the attempts per device. It loads every case, code and extraction of each organization on the page, so keep pages small. Super admin only.","operationId":"ReportAdminController_fetchOrgReport","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"searchText","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationReportEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the organization activity report (internal)","tags":["admin"],"x-internal":true}},"/admin/report/last-day-report":{"get":{"description":"Returns yesterday's extraction attempts per code, the extraction codes created yesterday with their progress, and the data-source collections of the day; each part is null when there was nothing. Super admin only.","operationId":"ReportAdminController_fetchLastDayReport","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"extractions":{"type":"object","nullable":true,"description":"Extraction attempts per code; null when there were none.","properties":{"extractionCodes":{"type":"array","items":{"type":"object","properties":{"extractionCode":{"type":"string","nullable":false,"example":"AB12CD"},"codeCreatedByEmail":{"type":"string","nullable":false,"example":"paralegal@example.com"},"custodian":{"type":"string","nullable":false,"example":"Jane Doe"},"custodianEmail":{"type":"string","nullable":false,"example":"jane.doe@example.com"},"caseName":{"type":"string","nullable":false,"example":"Doe v. Roe"},"orgName":{"type":"string","nullable":false,"example":"Example Firm"},"hasBackupCompleted":{"type":"boolean","example":true},"hasConversationShared":{"type":"boolean","example":false},"dueDate":{"type":"string","nullable":true,"example":"01/31/2026","description":"MM/DD/YYYY."},"deviceType":{"type":"string","nullable":false,"example":"ios"},"totalAttempt":{"type":"number","example":2},"totalSuccessAttempt":{"type":"number","example":1},"totalErrorAttempt":{"type":"number","example":1},"failedReasons":{"type":"string","nullable":false,"example":"Backup interrupted"},"isOldDevice":{"type":"number","enum":[0,1],"example":0}}}}}},"newCodes":{"type":"object","nullable":true,"description":"Extraction codes created that day; null when there were none.","properties":{"extractionCodes":{"type":"array","items":{"type":"object","properties":{"extractionCode":{"type":"string","nullable":false,"example":"AB12CD"},"codeCreatedByEmail":{"type":"string","nullable":false,"example":"paralegal@example.com"},"custodian":{"type":"string","nullable":false,"example":"Jane Doe"},"custodianEmail":{"type":"string","nullable":false,"example":"jane.doe@example.com"},"caseName":{"type":"string","nullable":false,"example":"Doe v. Roe"},"orgName":{"type":"string","nullable":false,"example":"Example Firm"},"hasBackupCompleted":{"type":"boolean","example":true},"hasConversationShared":{"type":"boolean","example":false},"dueDate":{"type":"string","nullable":true,"example":"01/31/2026","description":"MM/DD/YYYY."},"organizationPlan":{"type":"string","nullable":false,"example":"Pay as you go"},"attachedDataSources":{"type":"string","nullable":false,"example":"Phone, Email"},"dataSourcesTotal":{"type":"number","example":2},"dataSourcesCompleted":{"type":"number","example":1}}}}}},"dataSources":{"type":"object","nullable":true,"description":"Data-source collections that day; null when there were none.","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"sourceName":{"type":"string","nullable":false,"example":"gmail"},"displayName":{"type":"string","nullable":false,"example":"Gmail"},"category":{"type":"string","nullable":false,"example":"email"},"sourceType":{"type":"string","nullable":false,"example":"email"},"status":{"type":"string","nullable":false,"example":"completed"},"caseName":{"type":"string","nullable":false,"example":"Doe v. Roe"},"orgName":{"type":"string","nullable":false,"example":"Example Firm"},"custodian":{"type":"string","nullable":false,"example":"Jane Doe"},"extractionCode":{"type":"string","nullable":false,"example":"AB12CD"},"itemsCount":{"type":"number","example":340},"totalSize":{"type":"number","example":52428800},"createdBy":{"type":"string","nullable":false,"example":"paralegal@example.com"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get yesterday's extraction report (internal)","tags":["admin"],"x-internal":true}},"/admin/report/trigger-last-day-report":{"get":{"description":"Builds yesterday's extraction report and posts it to the internal notification channel; although it is a GET, every call sends the report again. Returns `isSuccess: false` when there were no extractions or new codes. Super admin only.","operationId":"ReportAdminController_triggerLastDayReport","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Send yesterday's extraction report (internal)","tags":["admin"],"x-internal":true}},"/admin/report/extractions":{"get":{"description":"Lists data-source collection statuses that have an extraction code, newest first, one page at a time, filtered by `status` (in progress by default), `organizationId`, `extractionCode`, a date range on the last status change and the `watchlist` and `isVIP` switches. Each row carries the code, status, source type, dates, organization name and email and the code's watchlist flag; an unknown `extractionCode` returns an empty page. Super admin only.","operationId":"ReportAdminController_fetchExtractionsReport","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"organizationId","required":false,"in":"query","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"default":["in_progress"],"type":"array","items":{"type":"string","enum":["pending","in_progress","staged","completed","failed"]}}},{"name":"startDate","required":false,"in":"query","schema":{"type":"string"}},{"name":"endDate","required":false,"in":"query","schema":{"type":"string"}},{"name":"isVIP","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}},{"name":"watchlist","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionStatusItemEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List extraction statuses (internal)","tags":["admin"],"x-internal":true}},"/admin/report/user-email-data-providers":{"post":{"description":"Lists email accounts connected through extraction codes, one row per account and code, with the provider, the code and its creator's email, the case and organization names and the number of imported threads, filtered by connection date (`startDate`/`endDate`), `extractionCode` or `caseId`. An unknown `extractionCode` returns an empty list. Super admin only.","operationId":"ReportAdminController_fetchUserEmailDataProviderReport","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FetchUserEmailDataProviderReportDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"},"email":{"type":"string","nullable":false,"example":"jane.doe@example.com"},"provider":{"type":"string","nullable":false,"example":"gmail"},"extractioncode":{"type":"string","nullable":false,"example":"AB12CD"},"extractioncodeCreatorEmail":{"type":"string","nullable":false,"example":"paralegal@example.com"},"case":{"type":"string","nullable":false,"example":"Doe v. Roe"},"organization":{"type":"string","nullable":false,"example":"Example Firm"},"threadsCount":{"type":"number","example":214}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List connected email accounts (internal)","tags":["admin"],"x-internal":true}},"/admin/report/phone-device-storage-logs":{"get":{"description":"Lists the storage readings phones reported during extraction (calculated and user-reported space used and disk size, OS and hardware versions), filtered by `deviceId` and a date range, one page at a time. Super admin only.","operationId":"ReportAdminController_fetchPhoneDeviceStorageLogsReport","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"deviceId","required":false,"in":"query","schema":{"type":"string"}},{"name":"startDate","required":false,"in":"query","schema":{"type":"string"}},{"name":"endDate","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PhoneDeviceStorageLogItemEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List phone storage logs (internal)","tags":["admin"],"x-internal":true}},"/admin/report/notifications":{"get":{"description":"Lists internal notifications newest first, one page at a time, optionally only those whose thread belongs to `organizationId`, `caseId` or `extractionCodeId`, each with the device and data source it refers to. Super admin only.","operationId":"ReportAdminController_fetchNotificationsReport","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"organizationId","required":false,"in":"query","schema":{"type":"string"}},{"name":"caseId","required":false,"in":"query","schema":{"type":"string"}},{"name":"extractionCodeId","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/NotificationEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List notifications (internal)","tags":["admin"],"x-internal":true}},"/admin/payment/update-latest-sub-for-org":{"post":{"description":"Reads the single Stripe subscription of the organization (given by `orgId`, or by its owner's email `orgOwnerEmail`) and records it on the organization's legacy plan; nothing changes when it is already recorded. Returns 400 when there is no subscription or several, or the owner owns several organizations, and 404 when the organization or owner is not found. Super admin only.","operationId":"PaymentAdminController_updateLatestSubForOrg","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLatestSubscriptionForOrgDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Sync an organization's legacy subscription (internal)","tags":["admin"],"x-internal":true}},"/admin/payment/update-org-plan":{"post":{"deprecated":true,"description":"Moves the organization (given by `orgId`, or by its owner's email `orgOwnerEmail`) to the legacy subscription product `productId`, invoicing the prorated difference at once, and returns the organization's details. Legacy billing only; scheduled for removal. Returns 404 when the organization or owner is not found. Super admin only.","operationId":"PaymentAdminController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrgPlanDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationDetailsEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Change an organization's legacy plan (internal)","tags":["admin"],"x-internal":true}},"/admin/tags/migrate":{"post":{"description":"Queues a job that backfills the tag links (`attachedTags`) from the legacy tag fields of the listed `collections` (every registered collection when omitted), `batchSize` documents at a time (50 to 5000, 1000 by default), optionally as a `dryRun`. Returns the queued job, or 400 for a collection that is not registered. Super admin only.","operationId":"TagMigrationAdminController_trigger","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerTagMigrationDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/TagMigrationStatusEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a tag migration (internal)","tags":["admin"],"x-internal":true}},"/admin/tags/migrate/{jobId}":{"get":{"description":"Returns the job status and the progress of each collection (total, processed, skipped, errored, start and end) with the last error. Returns 404 for an unknown job. Super admin only.","operationId":"TagMigrationAdminController_status","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/TagMigrationStatusEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a tag migration job (internal)","tags":["admin"],"x-internal":true}},"/admin/tags/migrate/{jobId}/cancel":{"post":{"description":"Marks the job as cancelled and returns its status. Returns 404 for an unknown job. Super admin only.","operationId":"TagMigrationAdminController_cancel","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/TagMigrationStatusEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Cancel a tag migration job (internal)","tags":["admin"],"x-internal":true}},"/admin/email/{caseId}/imported-emails/{providerId}":{"delete":{"description":"Deletes every email thread, email and attachment imported into the case from email account `providerId`, then rolls back the case's collection status for that account. Runs synchronously and reports how many threads were deleted; returns 404 for an unknown case or account. Super admin only.","operationId":"EmailAdminController_deleteImportedEmails","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case the emails were imported into","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","description":"Id of the UserEmailDataProvider (the imported email account)","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a mailbox's imported emails from a case (internal)","tags":["admin"],"x-internal":true}},"/admin/email/backfill-tag-sources":{"post":{"description":"Adds to each tag the mailboxes of the emails it is attached to, for one case (`caseId`) or for all cases; it only adds, so it is safe to repeat. Runs synchronously and reports how many tags changed. Super admin only.","operationId":"EmailAdminController_backfillTagSources","parameters":[],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackfillTagSourcesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Backfill tag mailboxes (internal)","tags":["admin"],"x-internal":true}},"/admin/email/{caseId}/fix-search-tag-attribution":{"post":{"description":"For emails imported into the case by a multi-keyword search, removes the keyword tags whose keyword does not appear in the email (sender, recipient, subject and manual tags stay) and recomputes the thread tags, optionally only for mailbox `providerId`. Safe to repeat; runs synchronously and reports how many emails and threads changed. Super admin only.","operationId":"EmailAdminController_fixSearchTagAttribution","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case to fix","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixSearchTagAttributionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Fix keyword tags on search imports (internal)","tags":["admin"],"x-internal":true}},"/admin/case-files/{caseId}/delete":{"post":{"description":"Soft-deletes files uploaded to the case through the portal or by custodians, either the ids in `caseFileIds` or all of them with `all`; documents synced from a legal integration are not touched. Deleted files leave every case view and the files data source counts go down. Returns how many files matched, already deleted ones included, and how many this call deleted, so a retry deletes nothing. Returns 404 for an unknown case or when any id is not an uploaded file on the case. Super admin only.","operationId":"CaseFileAdminController_deleteCaseFiles","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case the files were uploaded to","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteCaseFilesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeleteCaseFilesResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete uploaded case files (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/billing-profile":{"patch":{"description":"Changes the tier, billing mode, billing email, card-on-file flag, charge trigger or price overrides of an organization already on case billing. Only the fields sent change; overrides merge into the existing ones, and a `null` SKU price removes that override. Returns the saved profile with warnings, for example when an override will bill a SKU the tier normally includes. Returns 404 when the organization has no billing profile and 400 for an invalid tier, mode and cadence combination or any change of cadence. Super admin only.","operationId":"BillingAdminController_updateBillingProfile","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminBillingProfileDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminBillingProfileResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update an organization's billing profile (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/migrate-billing":{"post":{"description":"Creates the organization's billing profile with the given tier, billing mode and cadence, moves its legacy subscription's card onto the customer when it can, then cancels the legacy subscription. Returns whether a card is on file and whether a legacy subscription was cancelled. Returns 404 for an unknown organization, 409 when it is already on case billing and 400 for an invalid tier, mode and cadence combination. Super admin only.","operationId":"BillingAdminController_migrateOrgBilling","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrateOrgBillingDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminOrgBillingMigrationResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Move an organization to case billing (internal)","tags":["admin"],"x-internal":true}},"/admin/cases/{caseId}/billing-items":{"post":{"description":"Records a staff add-on on the case: `quantity` of the SKU at its price or at `unitAmountCentsOverride`. With `comped` the item is waived at once; otherwise a SKU billed immediately is charged right away and the rest wait for the daily sweep. Sending the same `requestId` again returns the existing item instead of charging twice. Returns 404 for an unknown case or when its organization has no billing profile and 400 when the SKU cannot be priced. Super admin only.","operationId":"BillingAdminController_createBillingItem","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAdminBillingItemDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminBillingItemResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Add a billing item to a case (internal)","tags":["admin"],"x-internal":true}},"/admin/cases/{caseId}/pro-upgrade":{"post":{"description":"Turns on pro for the case and grants its AI tokens at once, recorded as a comped pro-upgrade billing item that is never charged. Returns 404 for an unknown case or when its organization has no billing profile, and 409 when the case already has a pro-upgrade item (comp that item instead). Super admin only.","operationId":"BillingAdminController_compedProUpgrade","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProUpgradeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminBillingItemResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Give a case a free pro upgrade (internal)","tags":["admin"],"x-internal":true}},"/admin/billing-items/{id}/comp":{"post":{"description":"Waives a billing item that is pending, parked as a draft or failed to collect, so it is never charged; comping a pro-upgrade item also grants pro to its case. Returns 404 for an unknown item and 409 when the item has any other status or changed at the same time. Super admin only.","operationId":"BillingAdminController_compBillingItem","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the billing item","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminBillingItemActionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminBillingItemResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Comp a billing item (internal)","tags":["admin"],"x-internal":true}},"/admin/billing-items/{id}/void":{"post":{"description":"Cancels a billing item that is pending, parked as a draft or failed to collect, and frees its idempotency key so the same charge can be recorded again. Returns 404 for an unknown item and 409 when the item has any other status or changed at the same time. Super admin only.","operationId":"BillingAdminController_voidBillingItem","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the billing item","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminBillingItemActionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminBillingItemResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Void a billing item (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/token-grants":{"post":{"description":"Adds `tokens` to the case's AI token allowance and returns the new allowance and usage. When the organization is on case billing the grant also shows on its statement as a $0 item, and a repeated `requestId` does not grant again. Returns 404 for an unknown case. Super admin only.","operationId":"BillingAdminController_grantTokens","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantAiTokensDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminTokenGrantResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Grant AI tokens to a case (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/plan":{"post":{"description":"Sets the organization's self-serve tier and cadence (pay as you go), with the same proration as the customer flow, or assigns its first plan when it has none. Cadence defaults to monthly; an annual-only tier is always annual and returns 400 for any other cadence. Returns the plan, the proration in cents and whether the organization still has to add a card, which staff cannot do here. Super admin only.","operationId":"BillingAdminController_changePlan","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminChangePlanDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminPlanChangeResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Change an organization's plan (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/billing":{"get":{"description":"Returns the organization's current plan (tier, cadence, billing mode and card on file), or null when it is not on case billing, its use of seats, storage, cases and AI tokens against their caps or budget, and whether its base fee is overdue. Super admin only.","operationId":"BillingAdminController_getBilling","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BillingPlanCurrentResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization's plan and usage (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/billing/statement":{"get":{"description":"Returns the organization's billing items grouped by case, with organization-wide charges in their own group, each group with its rows and the subtotal charged. Page with `limit` and `skip`. Super admin only.","operationId":"BillingAdminController_getStatement","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrgReconciliationResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization's billing statement (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/credits":{"post":{"description":"Adds `amountCents` of credit to the organization's balance without any payment and returns the new balance with the latest 50 ledger entries. A repeated `requestId` does not grant again. Returns 403 while credits are turned off. Super admin only.","operationId":"BillingAdminController_grantCredits","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantCreditDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CreditBalanceResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Grant credit to an organization (internal)","tags":["admin"],"x-internal":true},"get":{"description":"Returns the organization's credit balance, its deduction percentage and its credit ledger entries (`limit` default 50, `skip`). Returns 403 while credits are turned off. Super admin only.","operationId":"BillingAdminController_getCredits","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CreditBalanceResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization's credit balance (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/prepaid":{"get":{"description":"Returns the organization's prepaid settings (null when it has none), its remaining units per SKU and its prepaid ledger entries, newest first (`limit` default 20, `skip`). Super admin only.","operationId":"BillingAdminController_getPrepaid","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/PrepaidStateResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization's prepaid units (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/prepaid/override":{"post":{"description":"Turns the unpaid override on or off for an organization on prepaid billing: with `allowUnpaid` its collections go through without prepaid units, and `reason` is stored while the override is on. Returns the prepaid state with the latest 20 ledger entries. Returns 404 when the organization has no billing profile and 400 when it is not on prepaid billing. Super admin only.","operationId":"BillingAdminController_setPrepaidOverride","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePrepaidOverrideDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/PrepaidStateResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Waive prepaid units for an organization (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/sweep-runs":{"get":{"description":"Lists the recorded daily billing sweep runs, nightly and admin-triggered, most recent first, with timings, status, counts and failed groups. Page with `limit` (default 50) and `skip`. Super admin only.","operationId":"BillingAdminController_listSweepRuns","parameters":[{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingSweepRunResponseEntity"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List daily billing sweep runs (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/cron/daily-sweep":{"post":{"description":"Runs the daily billing sweep (base fees, recurring add-ons, then invoicing of pending items) as of `asOf` or now, for every organization or only `organizationId`, records the run and returns its summary. Not available in production (403). Super admin only.","operationId":"BillingAdminController_triggerDailySweep","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerBillingSweepDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"invoicesCreated":{"type":"integer","example":3},"totalCents":{"type":"integer","example":45000},"groupsFailed":{"type":"integer","example":0},"failedGroups":{"type":"array","description":"Which group failed and why; empty on a clean run.","items":{"type":"string"},"example":[]},"stuckReset":{"type":"integer","example":0},"baseFeeRowsCreated":{"type":"integer","example":2},"recurringAddonRowsCreated":{"type":"integer","example":1}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Run the daily billing sweep now (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/cron/ai-overage-close":{"post":{"description":"Bills organizations on an AI tier for the AI tokens used in `yearMonth` (default: the current UTC month) above their budget, for every organization or only `organizationId`, and returns how many organizations were checked and how many overages were billed. Running it again for the same month bills nothing new. Not available in production (403). Super admin only.","operationId":"BillingAdminController_triggerAiOverageClose","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloseBillingMonthDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"orgsChecked":{"type":"integer","example":12},"overagesBilled":{"type":"integer","example":1}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Run the monthly AI overage close now (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/cron/storage-close":{"post":{"description":"Checks the storage used in `yearMonth` (default: the current UTC month) against each organization's storage cap, for every organization or only `organizationId`, and sends one alert listing the organizations over their cap; nothing is charged. Returns how many organizations were checked and how many were over. Not available in production (403). Super admin only.","operationId":"BillingAdminController_triggerStorageClose","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloseBillingMonthDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"orgsChecked":{"type":"integer","example":12},"breaches":{"type":"integer","example":1}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Run the monthly storage check now (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/cron/reconciliation":{"post":{"description":"Compares Stripe with the billing ledger as of `asOf` or now, across all organizations, and returns invoice and ledger mismatches, items stuck as invoiced, legacy subscription drift, lost pro grants and migrated organizations that still have a live legacy subscription. It also voids leftover empty draft invoices and lists them. Not available in production (403). Super admin only.","operationId":"BillingAdminController_triggerReconciliation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerReconciliationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"invoiceMismatches":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["orphan","amount"]},"invoiceId":{"type":"string"},"stripeTotalCents":{"type":"integer"},"ledgerSumCents":{"type":"integer","nullable":true}}}},"stuckInvoiced":{"type":"array","items":{"type":"object","properties":{"billingItemId":{"type":"string"},"invoiceId":{"type":"string","nullable":true},"ageMs":{"type":"integer"}}}},"subscriptionDrift":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["missing","status"]},"subscriptionId":{"type":"string"},"stripeStatus":{"type":"string"},"localStatus":{"type":"string","nullable":true}}}},"voidedEmptyDrafts":{"type":"array","items":{"type":"object","properties":{"invoiceId":{"type":"string"},"ageMs":{"type":"integer"}}}},"lostProGrants":{"type":"array","items":{"type":"object","properties":{"billingItemId":{"type":"string"},"caseId":{"type":"string","nullable":true},"invoiceId":{"type":"string","nullable":true}}}},"migratedOrgsWithLiveLegacySub":{"type":"array","items":{"type":"object","properties":{"organizationId":{"type":"string"},"subscriptionExternalId":{"type":"string","nullable":true},"status":{"type":"string"}}}}},"example":{"invoiceMismatches":[],"stuckInvoiced":[],"subscriptionDrift":[],"voidedEmptyDrafts":[],"lostProGrants":[],"migratedOrgsWithLiveLegacySub":[]}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Run billing reconciliation now (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/coupons":{"post":{"description":"Creates the coupon in Stripe, keeps a local copy for search and returns it. Send exactly one of `percentOff` or `amountOffCents` (with `currency`); `durationInMonths` is required with `duration: repeating` and not allowed otherwise. Breaking either rule returns 400. Super admin only.","operationId":"BillingCouponAdminController_createCoupon","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCouponDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminCouponResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a coupon (internal)","tags":["admin"],"x-internal":true},"get":{"description":"Lists coupons newest first with the total that match, filtered by `status` (active, expired or deleted, worked out from the expiry and deletion dates), `duration`, creation date range and a case-insensitive `search` on code and name. `limit` defaults to 50 (at most 200). Super admin only.","operationId":"BillingCouponAdminController_listCoupons","parameters":[{"name":"status","required":false,"in":"query","description":"active = not deleted and not past redeemBy; expired = past redeemBy; deleted = withdrawn in Stripe","schema":{"type":"string","enum":["active","expired","deleted"]}},{"name":"search","required":false,"in":"query","description":"Case-insensitive substring over coupon code and name","schema":{"type":"string"}},{"name":"duration","required":false,"in":"query","schema":{"type":"string","enum":["once","repeating","forever"]}},{"name":"createdFrom","required":false,"in":"query","description":"Created at or after (ISO 8601)","schema":{"format":"date-time","type":"string"}},{"name":"createdTo","required":false,"in":"query","description":"Created at or before (ISO 8601)","schema":{"format":"date-time","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminCouponListResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List coupons (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/coupons/{couponId}":{"get":{"description":"Returns one coupon by its code, deleted ones included. Returns 404 for an unknown code. Super admin only.","operationId":"BillingCouponAdminController_getCoupon","parameters":[{"name":"couponId","required":true,"in":"path","description":"Stripe coupon id / redeemable code","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminCouponResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a coupon (internal)","tags":["admin"],"x-internal":true},"delete":{"description":"Deletes the coupon in Stripe so it can no longer be applied and marks the local copy deleted; discounts already on a customer or an invoice stay. Returns the coupon, or 404 for an unknown code. Super admin only.","operationId":"BillingCouponAdminController_deleteCoupon","parameters":[{"name":"couponId","required":true,"in":"path","description":"Stripe coupon id / redeemable code","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminCouponResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete a coupon (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/coupon":{"post":{"description":"Puts the coupon on top of the organization's coupon stack, making it the active discount on the organization's Stripe customer while earlier coupons stay beneath it, and returns the stack. Returns 404 when the organization has no Stripe customer or the coupon is unknown, and 400 when the coupon is expired, deleted or already on the stack. Super admin only.","operationId":"BillingCouponAdminController_applyCoupon","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyCouponDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminCouponStackResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Apply a coupon to an organization (internal)","tags":["admin"],"x-internal":true},"delete":{"description":"Removes the top coupon from the organization's stack and makes the next eligible coupon beneath it active, or clears the discount when none is left, then returns the stack. Returns 404 when the organization has no Stripe customer or no coupons on its stack. Super admin only.","operationId":"BillingCouponAdminController_popTopCoupon","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminCouponStackResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove the active coupon (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/coupon/{couponId}":{"delete":{"description":"Removes the named coupon wherever it sits in the organization's stack, then makes the top eligible coupon the active discount, and returns the stack. Returns 404 when the organization has no Stripe customer or the coupon is not on its stack. Super admin only.","operationId":"BillingCouponAdminController_removeStackedCoupon","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"couponId","required":true,"in":"path","description":"Stripe coupon id / redeemable code","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminCouponStackResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove a coupon from the stack (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/coupons":{"get":{"description":"Lists the coupons on the organization's stack, top first, each with its status, whether it could be the active discount and whether it is, plus the id of the active coupon. Super admin only.","operationId":"BillingCouponAdminController_getOrgCouponStack","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminCouponStackResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an organization's coupon stack (internal)","tags":["admin"],"x-internal":true}},"/admin/billing-items/drafts":{"get":{"description":"Lists billing items parked as drafts, newest first, across all organizations or narrowed by `organizationId` and `caseId`, with the total. `limit` defaults to 20 (at most 100). Super admin only.","operationId":"BillingDraftAdminController_listDrafts","parameters":[{"name":"organizationId","required":false,"in":"query","description":"Only this organization","schema":{"type":"string"}},{"name":"caseId","required":false,"in":"query","description":"Only this case","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"maximum":100,"default":20,"type":"number"}},{"name":"skip","required":false,"in":"query","schema":{"default":0,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingItemResponseEntity"}},"total":{"type":"integer","example":1}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List draft billing items (internal)","tags":["admin"],"x-internal":true}},"/admin/billing-items/{id}/draft":{"post":{"description":"Parks a pending billing item so the invoicing sweep skips it until it is released; it still counts as owed on statements. Returns 404 for an unknown item, 400 when the item is not pending and 409 when it changed at the same time. Super admin only.","operationId":"BillingDraftAdminController_draftBillingItem","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the billing item","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminBillingItemActionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminBillingItemResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Park a billing item as a draft (internal)","tags":["admin"],"x-internal":true}},"/admin/billing-items/{id}/ready":{"post":{"description":"Returns a draft billing item to pending so it bills on the next sweep. Returns 404 for an unknown item, 400 when the item is not a draft and 409 when it changed at the same time. Super admin only.","operationId":"BillingDraftAdminController_readyBillingItem","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the billing item","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseDraftBillingItemDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminBillingItemResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Release a draft billing item (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/report":{"post":{"description":"Builds a billing report for the organizations in `orgIds` (default: every organization on case billing), narrowed by `caseIds` and the date range. In JSON format the report comes back in this response, with each organization's charges and the grand total charged; in PDF format it is queued and this returns its id and status to poll. Returns 400 for invalid filters and 404 while billing reports are turned off. Super admin only.","operationId":"BillingReportAdminController_createReport","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminBillingReportQueryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/BillingReportResponseEntity"},{"$ref":"#/components/schemas/BillingReportStatusResponseEntity"}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Build a billing report across organizations (internal)","tags":["admin"],"x-internal":true}},"/admin/billing/report/{reportId}":{"get":{"description":"Returns the status of a PDF billing report the caller requested, with a temporary download link once it is ready, the error when it failed and the organizations left out because they could not be rendered. Returns 404 for an unknown report or while billing reports are turned off, and 403 for a report another admin requested. Super admin only.","operationId":"BillingReportAdminController_getReportStatus","parameters":[{"name":"reportId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BillingReportStatusResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a queued billing report (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/recurring-addons":{"post":{"description":"Starts a recurring add-on for the organization (kit or laptop rental billed monthly, live bank every six months), optionally tied to `case`, with periods anchored on the day of `startedAt` (now by default). Repeating the same organization, SKU and `externalRef` returns the existing record instead of starting a second one; returns 400 for a SKU that is not a recurring add-on or an organization without a case-billing profile. Super admin only.","operationId":"RecurringAddonAdminController_activate","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivateRecurringAddonDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminRecurringAddonResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a recurring add-on (internal)","tags":["admin"],"x-internal":true},"get":{"description":"Lists the organization's recurring add-ons, newest first, optionally only those with `status`. Super admin only.","operationId":"RecurringAddonAdminController_list","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","description":"Omit to return both active and canceled subscriptions","schema":{"type":"string","enum":["active","canceled"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminRecurringAddonListResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List recurring add-ons (internal)","tags":["admin"],"x-internal":true}},"/admin/orgs/{orgId}/recurring-addons/{addonId}":{"delete":{"description":"Stops the future periods of the add-on; the period under way is still billed in full, so this is not a refund. Cancelling an already cancelled add-on changes nothing; returns 404 when the add-on does not belong to the organization. Super admin only.","operationId":"RecurringAddonAdminController_cancel","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}},{"name":"addonId","required":true,"in":"path","description":"Id of the recurring add-on record","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRecurringAddonDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminRecurringAddonResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Cancel a recurring add-on (internal)","tags":["admin"],"x-internal":true}},"/admin/feature-flags":{"get":{"description":"Lists every feature flag this build defines plus any leftover rows, unpaginated. `status` says whether a flag has its database row (SEEDED), is still waiting on its seed migration (UNSEEDED, reads as off) or is a leftover from a retired flag (ORPHANED). Super admin only.","operationId":"FeatureFlagAdminController_listFeatureFlags","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminFeatureFlagListResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List feature flags (internal)","tags":["admin"],"x-internal":true}},"/admin/feature-flags/{key}":{"patch":{"description":"Sets the flag and records who changed it; the node that served the request applies it at once and other nodes within about 45 seconds. Sending the current value changes nothing. Returns 404 for a key this build does not define, including an ORPHANED one. Super admin only.","operationId":"FeatureFlagAdminController_updateFeatureFlag","parameters":[{"name":"key","required":true,"in":"path","description":"The flag key as defined in the FeatureFlag enum, e.g. \"clio-integration\"","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFeatureFlagDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AdminFeatureFlagResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}},"404":{"description":"No such feature flag in this build"}},"security":[{"access-token":[]}],"summary":"Turn a feature flag on or off (internal)","tags":["admin"],"x-internal":true}},"/extraction/device/file-hash":{"post":{"description":"Desktop plumbing: stores or updates the hash of each listed backup file for the device on this computer, and records the counts in the device activity log.","operationId":"ExtractionController_addUpdateFileHash","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUpdateDeviceFileHashDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Save device file hashes (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/match-file-hash":{"post":{"description":"Desktop plumbing: checks the listed backup file hashes against the ones stored for the device on this computer, records the result in the device activity log and returns it as `matched`.","operationId":"ExtractionController_matchFileHash","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchDeviceFileHashDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MatchDeviceFileHashResultEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Match device file hashes (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/app-general-logs":{"post":{"description":"Desktop plumbing: takes a log file (multipart field `file`), zips it with the request details, stores it in S3 as a file record and posts a Slack notification with its download link for the support team. An extraction code in the body, if valid, is added to the notification.","operationId":"ExtractionController_uploadLogsOpen","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadLogOpenDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Upload app logs without a session (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/app-logs":{"post":{"description":"Desktop plumbing: takes a log file (multipart field `file`), zips it with the request details, stores it in S3 and posts a Slack notification with its download link and the device, OS and backup details. Also saves an app log record for the caller's extraction code and case. Returns the Slack message ts as `threadTs`.","operationId":"ExtractionController_uploadAppLogs","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadAppLogDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UploadDeviceLogResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Upload app logs (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/app-general-logs/v2":{"post":{"description":"Desktop plumbing: same as app-general-logs, but the log file is one the app already uploaded to S3 (`fileProviderId`); it is downloaded, zipped with the request details, stored again and announced on Slack.","operationId":"ExtractionController_uploadLogsOpenV2","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadLogOpenDtoV2"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Upload app logs from S3 without a session (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/app-logs/v2":{"post":{"description":"Desktop plumbing: same as app-logs, but the log file is one the app already uploaded to S3 (`fileProviderId`). Returns the Slack message ts as `threadTs`.","operationId":"ExtractionController_uploadAppLogsV2","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadAppLogDtoV2"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UploadDeviceLogResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Upload app logs from S3 (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/extraction-status":{"post":{"description":"Desktop plumbing: for each listed device, returns when it last finished a backup with the caller's extraction code on that computer, and whether the code's collection settings have changed since (`hasNewEcCodeConfig`). A device with no finished backup gets `lastBackupAt: null`.","operationId":"ExtractionController_fetchDevicesExtractionStatus","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FetchDevicesExtractionStatusDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DevicesExtractionStatusResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the backup status of devices (internal)","tags":["extraction"],"x-internal":true}},"/extraction/auth/logout":{"post":{"description":"Ends the current extraction-code session by deleting the session of the bearer token. `data` is always an empty object.","operationId":"ExtractionController_logout","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"Always empty","properties":{}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]},{"access-token":[]}],"summary":"Sign out","tags":["extraction"]}},"/extraction/auth/refresh":{"post":{"description":"Issues a new access token for a refresh token and returns it with the same refresh token. Returns 404 for an unknown refresh token and 400 when it has expired. The new token does not carry the extraction code, so extraction routes reject it with 401; an extraction-code session signs in again instead.","operationId":"ExtractionController_refresh","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RefreshTokenResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Refresh the access token (internal)","tags":["extraction"],"x-internal":true}},"/extraction/auth/login-with-extraction":{"post":{"description":"Signs in with an extraction code and returns access and refresh tokens, the code's user profile, the extraction code with its data sources, its case, the saved app config (when one is sent) and whether collection events are on. Creates a session and records the login for collection tracking. Inactive, locked, unreviewed or rejected codes get 401, and a code used from the wrong white-label app gets 400.","operationId":"ExtractionController_loginWithExtraction","parameters":[{"name":"user-agent","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginExtractionCodeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LoginResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Sign in with an extraction code","tags":["extraction"]}},"/extraction/aws/s3-user-data-upload-credentials":{"post":{"description":"Desktop plumbing: returns temporary AWS credentials, with the bucket, region and API version, for uploading collected data to S3. The plan checked is the one of the extraction code's organization, whatever organization the request names.","operationId":"ExtractionController_getTemporaryUserDataUploadToken","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AwsTemporaryS3CredentialEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get temporary S3 upload credentials (internal)","tags":["extraction"],"x-internal":true}},"/extraction/conversation/bulk-import":{"post":{"description":"Desktop plumbing: queues the uploaded conversations for background import into the caller's case. When search data is sent, it is also saved as a search-tracking record for the extraction code.","operationId":"ExtractionController_bulkUpload","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"description":"Bulk upload conversation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadBulkConversationDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Import uploaded conversations (internal)","tags":["extraction"],"x-internal":true}},"/extraction/conversation/search-uploaded-conversations":{"get":{"description":"Desktop plumbing: lists the conversations already uploaded to the extraction code's case, newest first, optionally only one device's (`deviceExternalId`). The case is always the extraction code's own; a case in the query is ignored. At most 100 per page.","operationId":"ExtractionController_searchUploadedConversation","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"case","required":true,"in":"query","schema":{"type":"string"}},{"name":"deviceExternalId","required":false,"in":"query","schema":{"type":"string"}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UploadedConversation"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List uploaded conversations (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device-activity-log":{"post":{"description":"Desktop plumbing: records a device activity event (backup, conversation upload, plug-in or resync started/completed/failed). A repeat of the same event for the same device and computer within 10 seconds is ignored and answered with the message `Already added`.","operationId":"ExtractionController_addActivityLog","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddDeviceActivityLogDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Record device activity (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/backup-update":{"post":{"description":"Desktop plumbing: records a device backup lifecycle transition (started, completed, failed or cancelled). On `completed`, charges the collection when the organization is on backup-time charging. Always 200 once handled; `billingOutcome` says what the charge dispatch did, and a declined charge is a normal outcome, never an error.","operationId":"ExtractionController_deviceBackupUpdate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceBackupUpdateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DeviceBackupUpdateResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Report a device backup update (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/storage-updates":{"post":{"description":"Desktop plumbing: saves the storage figures the app calculated for a device, keyed by its device id. If the device already has a storage log, the previous figures are kept in its history and the user-reported figures are cleared.","operationId":"ExtractionController_updateDeviceStorage","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDeviceStorageDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Report device storage usage (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/storage-feedback":{"post":{"description":"Desktop plumbing: saves the storage figures the user reported on the device's storage log. Returns `isSuccess: false` when the device has no storage log yet.","operationId":"ExtractionController_recordDeviceStorageFeedback","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceStorageFeedbackDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Record user-reported device storage (internal)","tags":["extraction"],"x-internal":true}},"/extraction/device/add-update-device":{"post":{"description":"Desktop plumbing: creates the device from the app's device metadata, or updates it, links it to the caller's extraction code, organization and case, and creates or updates the case's data-source status for it. Returns the device without the lists of organizations and extraction codes it is linked to.","operationId":"ExtractionController_addUpdateDevice","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUpdateDeviceDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AddUpdateDeviceResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Register or update a device (internal)","tags":["extraction"],"x-internal":true}},"/extraction/user/profile":{"get":{"description":"Returns the user profile of the caller's extraction code, including its organization.","operationId":"ExtractionController_getProfile","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the signed-in user's profile","tags":["extraction"]},"patch":{"description":"Updates the profile of the caller's extraction-code user with the given fields and returns the updated profile.","operationId":"ExtractionController_updateProfile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update the signed-in user's profile","tags":["extraction"]}},"/extraction/case/metadata":{"patch":{"description":"Sets the given keys in the metadata of the caller's extraction code (other keys are kept) and returns the updated extraction code.","operationId":"ExtractionController_updateExtractionCodeMetadata","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateExtractionCodeMetadataDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExtractionCodeEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update extraction code metadata","tags":["extraction"]}},"/extraction/organization/{orgId}/extractor-usages-stat":{"get":{"description":"Desktop plumbing: returns plan usage for the organization of the caller's extraction code (seats, devices and storage used, plan flags), whether the device in the query is new to the organization, and the client payment details. The orgId path parameter is not used.","operationId":"ExtractionController_getExtractorOrgUsagesStat","parameters":[{"name":"deviceId","required":false,"in":"query","schema":{"type":"string"}},{"name":"deviceType","required":false,"in":"query","schema":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/OrganizationUsagesStatEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the organization's plan usage (internal)","tags":["extraction"],"x-internal":true}},"/extraction/organization/{orgId}/contact-admin":{"post":{"description":"Desktop plumbing: queues an email to the admins of the organization that owns the caller's extraction code, asking for more device capacity, with the caller's message or a default text. The organization id in the path is ignored.","operationId":"ExtractionController_contactAdminForDevice","parameters":[{"name":"orgId","required":true,"in":"path","description":"Id of the organization","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceRequestContactAdminDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Ask the organization's admins for more devices (internal)","tags":["extraction"],"x-internal":true}},"/extraction/notifications/update-notification":{"post":{"description":"Desktop plumbing: posts the given messages to the internal Slack thread of the device's backup and saves them as a notification record. Returns the Slack message `ts`; `data` is null or missing when the post failed or notifications are blocked for this extraction code and device.","operationId":"ExtractionController_postUpdateNotification","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostUpdateNotificationDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/SlackNotificationResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Post device messages to Slack (internal)","tags":["extraction"],"x-internal":true}},"/extraction/notifications/tracking-notification":{"post":{"description":"Desktop plumbing: posts the given messages, with OS and extraction-code details, to the internal Slack thread for this computer and saves them as a notification record. Returns the Slack message `ts`; `data` is null or missing when the post failed.","operationId":"ExtractionController_postTrackingNotification","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostTrackingNotificationDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/SlackNotificationResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Post tracking messages to Slack (internal)","tags":["extraction"],"x-internal":true}},"/extraction/notifications/conversation-uploads":{"post":{"description":"Desktop plumbing: posts a conversation upload status (message, conversation count, app version and device) to the internal Slack thread of the device's backup, and saves it, with any message selection report, as a notification record. Returns the Slack message `ts`; `data` is null or missing when the post failed.","operationId":"ExtractionController_postConversationUploadNotifications","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadConversationsNotificationsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/SlackNotificationResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Post a conversation upload status to Slack (internal)","tags":["extraction"],"x-internal":true}},"/extraction/notifications/debug-logs-upload":{"post":{"description":"Desktop plumbing: creates a file record for a debug bundle the app already uploaded to S3 (`logPath`) and posts a Slack notification with its download link. Returns 404 if the file is not in S3.","operationId":"ExtractionController_postDebugLogNotifications","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostDebugLogNotificationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Register an uploaded debug bundle (internal)","tags":["extraction"],"x-internal":true}},"/extraction/app-settings/status":{"get":{"description":"Desktop plumbing: returns whether local conversion, local compression and collection events are turned on.","operationId":"ExtractionController_getAppSettingsStatus","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AppSettingsResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get desktop app settings (internal)","tags":["extraction"],"x-internal":true}},"/extraction/case/import-files":{"post":{"description":"Adds files the caller already uploaded to S3 to the extraction code's case, with an optional category; keys not found in S3 are skipped. Marks the case's files data source as completed and reports the collection to billing.","operationId":"ExtractionController_importCaseFiles","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddCaseFilesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Add uploaded files to the case","tags":["extraction"]}},"/extraction/case/import-media":{"post":{"description":"Desktop plumbing: registers the device on the caller's case and stores a record for each media file, linking its original and thumbnail already uploaded to S3. Returns how many records were imported.","operationId":"ExtractionController_importCaseMedia","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportCaseMediaDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Import device media records (internal)","tags":["extraction"],"x-internal":true}},"/extraction/case/import-notes":{"post":{"description":"Desktop plumbing: registers the device on the caller's case and stores its notes, linking any attachments already uploaded to S3. Returns how many notes were imported.","operationId":"ExtractionController_importCaseNotes","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportCaseNotesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Import device notes (internal)","tags":["extraction"],"x-internal":true}},"/extraction/case/import-browser-history":{"post":{"description":"Desktop plumbing: registers the device on the caller's case, downloads the Safari History.db the app uploaded to S3 and imports its browsing history. The file key must be under the device's own path, otherwise 400.","operationId":"ExtractionController_importBrowserHistory","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportCaseBrowserHistoryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Import Safari browser history (internal)","tags":["extraction"],"x-internal":true}},"/extraction/case/import-call-logs":{"post":{"description":"Desktop plumbing: registers the device on the caller's case and stores the given call-log conversations and their calls. Returns how many conversations and messages were imported.","operationId":"ExtractionController_importCallLogs","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportCaseCallLogsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Import call logs (internal)","tags":["extraction"],"x-internal":true}},"/extraction/case/check-imported-files":{"get":{"description":"Desktop plumbing: returns how many case files were uploaded with the caller's extraction code and their total size in bytes.","operationId":"ExtractionController_checkImportedFiles","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseFilesImportedCountEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Count imported files (internal)","tags":["extraction"],"x-internal":true}},"/extraction/case/search-files-from-extraction-code":{"get":{"description":"Lists the case files uploaded with the caller's extraction code, newest first by default, with paging. The firm's tags are not included.","operationId":"ExtractionController_searchFilesFromExtractionCode","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":true,"in":"query","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"createdAt","type":"string","enum":["createdAt","name","type","size"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseFileEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List files uploaded with the extraction code","tags":["extraction"]}},"/extraction/case/email-providers":{"get":{"description":"Lists the mailboxes linked to the caller's extraction code. OAuth tokens and IMAP credentials are never returned.","operationId":"ExtractionController_searchEmailProviders","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEmailDataProviderEntity"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List the extraction code's mailboxes","tags":["extraction"]}},"/extraction/case/email-providers/{providerId}/folders":{"post":{"description":"Fetches the folders of a mailbox linked to the extraction code's case live from the email provider, saves them on the mailbox and returns them. Returns 404 for a mailbox that is not linked to the caller's extraction code.","operationId":"ExtractionController_fetchEmailProviderFolders","parameters":[{"name":"providerId","required":true,"in":"path","description":"Id of the email provider (UserEmailDataProvider)","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"INBOX"},"name":{"type":"string","example":"Inbox"},"isSystemFolder":{"type":"boolean","example":true},"backgroundColor":{"type":"string","nullable":true},"metadata":{"type":"object","description":"The folder as the provider returned it"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a mailbox's folders","tags":["extraction"]}},"/extraction/case/email-threads":{"get":{"description":"Lists the imported email threads from the mailboxes linked to the caller's extraction code. Asking for a mailbox outside that set returns 403. The firm's tags are not included.","operationId":"ExtractionController_listEmailThreads","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"folders","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"domains","required":false,"in":"query","description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeDomains","required":false,"in":"query","description":"Exclude items whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeTags","required":false,"in":"query","description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"participants","required":false,"in":"query","description":"Filter by participant email address(es), any-of.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeEmails","required":false,"in":"query","description":"Exclude items whose participant address matches any of these (any-of). Must not overlap `participants`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"starred","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"unread","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"untagged","required":false,"in":"query","description":"When truthy (1/true), return only threads with no tags attached. Takes precedence over the `tags` filter.","schema":{"type":"boolean"}},{"name":"startDate","required":false,"in":"query","description":"Only return threads whose `latestMessageReceivedDate` is at/after this instant (inclusive). Accepts an ISO-8601 date-time.","schema":{"format":"date-time","example":"2024-01-01T00:00:00.000Z","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"Only return threads whose `latestMessageReceivedDate` is at/before this instant (inclusive). Accepts an ISO-8601 date-time.","schema":{"format":"date-time","example":"2024-12-31T23:59:59.999Z","type":"string"}},{"name":"sourceEmails","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"latestMessageReceivedDate","type":"string","enum":["subject","latestMessageSentDate","latestMessageReceivedDate"]}},{"name":"includeLastMessage","required":false,"in":"query","description":"When truthy (1/true), populate lastMessageOrDraft (id/subject); otherwise it is returned as null. The message body is never included — use the thread `snippet` for previews, or fetch the body from the thread-emails endpoint.","schema":{"type":"boolean"}},{"name":"includeHidden","required":false,"in":"query","description":"When true, include hidden threads (hiddenAt != null). Default false: hidden threads are excluded from the listing.","schema":{"default":false,"type":"boolean"}},{"name":"sharedStatus","required":false,"in":"query","description":"Opposing-council only. Narrows to threads in one sharing state; ignored for OL/OG callers.","schema":{"type":"string","enum":["shared","pending","not_gated"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEmailDataThreadEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List email threads","tags":["extraction"]}},"/extraction/case/email-threads/{threadId}/emails":{"get":{"description":"Returns the emails of one thread, with their bodies. Returns 404 unless the thread is in a mailbox linked to the caller's extraction code. The firm's tags are not included.","operationId":"ExtractionController_getThreadEmails","parameters":[{"name":"threadId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"folders","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"domains","required":false,"in":"query","description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeDomains","required":false,"in":"query","description":"Exclude emails whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeTags","required":false,"in":"query","description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeEmails","required":false,"in":"query","description":"Exclude emails whose from/to/cc/bcc address matches any of these (any-of).","schema":{"type":"array","items":{"type":"string"}}},{"name":"starred","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"unread","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"untagged","required":false,"in":"query","description":"When truthy (1/true), return only emails with no tags attached. Takes precedence over the `tags` filter.","schema":{"type":"boolean"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"date","type":"string","enum":["date"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEmailDataEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List the emails in a thread","tags":["extraction"]}},"/extraction/case/emails/search":{"get":{"description":"Searches the emails in the mailboxes linked to the caller's extraction code. Bodies are not included. Asking for a mailbox outside that set returns 403. The firm's tags are not included.","operationId":"ExtractionController_searchEmails","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Text search applied to all indexed fields (subject, body, snippet, from/to/cc/bcc). Supports the same AND / OR / NOT / -prefix, parentheses, \"quoted phrases\" and wildcards as the boolean query parser.","schema":{"example":"(discriminate OR racist) AND (\"Larry Graham\" OR Robinson)","type":"string"}},{"name":"folders","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"domains","required":false,"in":"query","description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive. Requires the `domains` field on the emailtext Atlas index.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeDomains","required":false,"in":"query","description":"Exclude items whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeTags","required":false,"in":"query","description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"untagged","required":false,"in":"query","description":"When truthy (1/true), return only emails with no tags attached. Takes precedence over the `tags` filter.","schema":{"type":"boolean"}},{"name":"participants","required":false,"in":"query","description":"Filter by participant email address(es), any-of.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeEmails","required":false,"in":"query","description":"Exclude items whose participant address matches any of these (any-of). Must not overlap `participants`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"startDate","required":false,"in":"query","description":"Only return emails whose `date` is at/after this instant (inclusive). Accepts an ISO-8601 date-time.","schema":{"format":"date-time","example":"2024-01-01T00:00:00.000Z","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"Only return emails whose `date` is at/before this instant (inclusive). Accepts an ISO-8601 date-time.","schema":{"format":"date-time","example":"2024-12-31T23:59:59.999Z","type":"string"}},{"name":"sourceEmails","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"date","type":"string","enum":["date","subject"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEmailDataEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search emails","tags":["extraction"]}},"/extraction/case/email-domains":{"get":{"description":"Lists the domains in the from, to, cc and bcc addresses of the emails in the mailboxes linked to the caller's extraction code, with how many emails each appears in.","operationId":"ExtractionController_getEmailDomains","parameters":[{"name":"sourceEmails","required":false,"in":"query","description":"Restrict to these source mailboxes (provider emails). Omit to cover every mailbox in the case.","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/EmailDomainsResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List sender and recipient domains","tags":["extraction"]}},"/extraction/case/emails/{emailId}/thread":{"get":{"description":"Returns the thread an email belongs to and the ids of all its emails. Returns 404 unless the email is in a mailbox linked to the caller's extraction code. The firm's tags are not included.","operationId":"ExtractionController_getThreadByEmailId","parameters":[{"name":"emailId","required":true,"in":"path","schema":{"type":"string"}},{"name":"includeLastMessage","required":false,"in":"query","description":"When truthy (1/true), populate lastMessageOrDraft (id/subject) on the thread; otherwise it is returned as null. The message body is never included — fetch it from the thread-emails endpoint.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ThreadByEmailResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the thread of an email","tags":["extraction"]}},"/extraction/case/data-status-tree":{"get":{"description":"Returns the data-collection status of the extraction code's case as a tree, limited to data collected with the caller's extraction code.","operationId":"ExtractionController_fetchCaseDataStatusTree","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseDataStatusListTreeResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the collection status tree","tags":["extraction"]}},"/extraction/case/data-status-v2":{"get":{"description":"Returns the data-collection status of the extraction code's case grouped by data source, with item counts, limited to data collected with the caller's extraction code.","operationId":"ExtractionController_fetchCaseDataStatusV2","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CaseDataStatusV2ResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the collection status (v2)","tags":["extraction"]}},"/extraction/financial-data/link/token/create":{"post":{"description":"Creates a Plaid Link token for the caller's extraction code, used to open Plaid Link on the client. Returns 409 while the organization's Plaid onboarding is not complete.","operationId":"ExtractionController_createLinkToken","parameters":[{"name":"extraction-code","in":"header","description":"Extraction code associated with the case","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLinkTokenDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LinkTokenResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a Plaid Link token","tags":["extraction"]}},"/extraction/financial-data/item/public_token/exchange":{"post":{"description":"Exchanges the public token from Plaid Link, saves the bank connection for the caller's extraction code, updates the case's data-source status and queues fetching the accounts. The Plaid access token is never returned. Returns 409 while the organization's Plaid onboarding is not complete.","operationId":"ExtractionController_exchangePublicToken","parameters":[{"name":"extraction-code","in":"header","description":"Extraction code associated with the case","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangePublicTokenDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ExchangePublicTokenResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Connect a bank account through Plaid","tags":["extraction"]}},"/extraction/ai-chat/import":{"post":{"description":"Creates an import job in the extraction code's case for an uploaded AI chat export (Claude, ChatGPT or Gemini), marks the data source as pending and queues the import. Returns 400 if the file was already imported.","operationId":"ExtractionController_importAiChatData","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportAiChatDataDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AiChatImportJobEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Import an AI chat export","tags":["extraction"]}},"/extraction/linkedin-data/{caseId}/import":{"post":{"description":"Desktop plumbing: registers the uploaded LinkedIn export zip by its S3 key, creates an import job for it in the extraction code's case (the caseId must be that case, otherwise 403), marks the data source as pending and queues the import. If the file was already imported, returns 400, or, when incremental re-import is on and nothing new would be added, `imported: false` with no job created (also 201).","operationId":"ExtractionController_importLinkedInData","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportLinkedInDataExtractionDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/LinkedInImportJobEntity"},{"$ref":"#/components/schemas/LinkedInImportSkippedResponseEntity"}]},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Import a LinkedIn export (internal)","tags":["extraction"],"x-internal":true}},"/extraction/file/register":{"post":{"description":"Creates a file record for a file already uploaded to S3, by its key, so it can then be imported (for example an AI chat export). If a record for that key already exists, it is returned instead. Returns 404 if the key is not in S3. Only keys under ai-chat/, linkedin-imports/ or case-files/<the caller's code>/ can be registered (403 otherwise).","operationId":"ExtractionController_registerUploadedFile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fileKey":{"type":"string","description":"S3 file key (path in bucket)"},"type":{"type":"string","description":"Override content type. If not provided, uses S3 metadata.","nullable":true}},"required":["fileKey"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserFileEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Register an uploaded file","tags":["extraction"]}},"/clients/fetch-client-list":{"get":{"description":"Super admin only: lists desktop app installs, filterable by system id, extraction code and online status, with their extraction codes (and cases) and connected devices; skip/limit or page/perPage pagination.","operationId":"ClientController_fetchClientList","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"systemId","required":false,"in":"query","description":"systemId","schema":{"type":"string"}},{"name":"ecCode","required":false,"in":"query","description":"ecCode","schema":{"type":"string"}},{"name":"isOnline","required":false,"in":"query","description":"0 false, 1 true","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DesktopClientEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List connected desktop apps (internal)","tags":["clients"],"x-internal":true}},"/clients/fetch-client-logs":{"post":{"description":"Super admin only: asks an online desktop app to upload its app log, or a device log for `deviceId`/`deviceType`. Returns 404 for an unknown client and 400 when it is offline.","operationId":"ClientController_fetchClientLogs","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FetchClientLogDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Ask a desktop app for its logs (internal)","tags":["clients"],"x-internal":true}},"/clients/send-intercom-signal":{"post":{"description":"Super admin only: tells every online desktop app signed in with the extraction code to open the given support conversation. Returns 404 when no app on that code is online.","operationId":"ClientController_sendIntercomSignal","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendIntercomSignalDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Open a support conversation in the desktop app (internal)","tags":["clients"],"x-internal":true}},"/email-data/{caseId}/create-oauth-url":{"post":{"description":"Inbox-connect plumbing: returns the email provider's OAuth sign-in URL for linking a mailbox to the case. The caller opens it and sends the returned code to add-email-provider.","operationId":"EmailDataController_createOAuthUrl","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOAuthUrlDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CreateOAuthUrlResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start linking a mailbox (internal)","tags":["email-data"],"x-internal":true}},"/email-data/{caseId}/add-email-provider":{"post":{"description":"Inbox-connect plumbing: exchanges the OAuth code for a mailbox grant, links the mailbox to the case and returns it. Tokens are never returned.","operationId":"EmailDataController_createEmailProvider","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmailProviderDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEmailDataProviderEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Link an OAuth mailbox (internal)","tags":["email-data"],"x-internal":true}},"/email-data/{caseId}/add-imap-email-provider":{"post":{"description":"Inbox-connect plumbing: connects to the IMAP server to check the host and credentials, links the mailbox to the case and returns it. The credentials are stored encrypted and never returned.","operationId":"EmailDataController_createImapEmailProvider","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateImapEmailProviderDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEmailDataProviderEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Link an IMAP mailbox (internal)","tags":["email-data"],"x-internal":true}},"/email-data/{caseId}/search-remote-emails":{"post":{"description":"Inbox-connect plumbing: searches the emails in a linked OAuth mailbox at the provider, before anything is imported. Pass nextPageToken back as pageToken for the next page. An extraction-code user may only name a mailbox linked to their own code (404 otherwise).","operationId":"EmailDataController_searchRemoteEmails","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRemoteEmailsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RemoteEmailSearchResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search a linked mailbox's emails (internal)","tags":["email-data"],"x-internal":true}},"/email-data/{caseId}/fetch-folders-for-email-provider/{providerId}":{"post":{"description":"Inbox-connect plumbing: fetches the folders of a linked mailbox from the provider, saves them on the mailbox and returns them. Folder ids can be passed as folderIds when starting an import. An extraction-code user may only name a mailbox linked to their own code (404 otherwise).","operationId":"EmailDataController_fetchFoldersForEmailProvider","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"INBOX"},"name":{"type":"string","example":"Inbox"},"isSystemFolder":{"type":"boolean","example":true},"backgroundColor":{"type":"string","nullable":true},"metadata":{"type":"object","description":"The folder as the provider returned it"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a linked mailbox's folders (internal)","tags":["email-data"],"x-internal":true}},"/email-data/{caseId}/search-remote-email-threads":{"post":{"description":"Inbox-connect plumbing: searches the threads in a linked OAuth mailbox at the provider and marks which ones are already imported into the case. An extraction-code user may only name a mailbox linked to their own code (404 otherwise).","operationId":"EmailDataController_searchRemoteEmailThreads","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRemoteEmailsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RemoteEmailThreadSearchResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search a linked mailbox's threads (internal)","tags":["email-data"],"x-internal":true}},"/email-data/{caseId}/search-remote-imap-email-threads":{"post":{"description":"Inbox-connect plumbing: searches the threads in a linked IMAP mailbox and marks which ones are already imported into the case. An extraction-code user may only name a mailbox linked to their own code (404 otherwise).","operationId":"EmailDataController_searchRemoteImapEmailThreads","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRemoteEmailsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RemoteEmailThreadSearchResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search an IMAP mailbox's threads (internal)","tags":["email-data"],"x-internal":true}},"/email-data/{caseId}/start-import-emails":{"post":{"description":"Queues a job that imports emails from a linked mailbox into the case: chosen threads, the results of one or several searches, or everything (optionally limited to some folders). Returns the job; poll GET import-jobs/:jobId for progress. An extraction-code user may only name a mailbox linked to their own code (404 otherwise).","operationId":"EmailDataController_startImportEmails","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartImportEmailsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEmailImportJobEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start an email import","tags":["email-data"]}},"/email-data/{caseId}/resume-import-job/{jobId}":{"post":{"description":"Operator plumbing: re-queues a failed import job. It carries on after the emails already imported.","operationId":"EmailDataController_resumeImportJob","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Resume a failed import job (internal)","tags":["email-data"],"x-internal":true}},"/email-data/{caseId}/cancel-import-job/{jobId}":{"post":{"description":"Stops an import job that has not finished. Emails already imported stay in the case.","operationId":"EmailDataController_cancelImportJob","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Cancel an import job","tags":["email-data"]}},"/email-data/{caseId}/import-jobs":{"get":{"description":"Lists the email import jobs of a case, newest first by default, with their status and imported counts.","operationId":"EmailDataController_searchImportJobs","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"status","required":false,"in":"query","description":"Filter by job status","schema":{"type":"string","enum":["not_started","in_progress","completed","failed"]}},{"name":"providerId","required":false,"in":"query","description":"Filter by email provider ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEmailImportJobEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List email import jobs","tags":["email-data"]}},"/email-data/{caseId}/import-jobs/{jobId}":{"get":{"description":"Returns one import job with its status and imported counts. Clients poll this while an import runs.","operationId":"EmailDataController_getImportJob","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEmailImportJobEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an email import job","tags":["email-data"]}},"/email-data/{caseId}/email-providers":{"get":{"description":"Inbox-connect plumbing: lists the mailboxes linked to the case. Tokens and IMAP credentials are never returned.","operationId":"EmailDataController_searchEmailProviders","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEmailDataProviderEntity"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List linked mailboxes (internal)","tags":["email-data"],"x-internal":true}},"/local-emails/{caseId}/threads":{"get":{"description":"Lists the imported email threads of a case with subject, participants, dates and tags, optionally filtered by mailbox, folder, domain, participant, tag or date. Hidden threads are left out unless includeHidden is set. On a case shared with opposing counsel, each side sees only the threads it may see.","operationId":"LocalEmailDataController_listThreads","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"folders","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"domains","required":false,"in":"query","description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeDomains","required":false,"in":"query","description":"Exclude items whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeTags","required":false,"in":"query","description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"participants","required":false,"in":"query","description":"Filter by participant email address(es), any-of.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeEmails","required":false,"in":"query","description":"Exclude items whose participant address matches any of these (any-of). Must not overlap `participants`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"starred","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"unread","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"untagged","required":false,"in":"query","description":"When truthy (1/true), return only threads with no tags attached. Takes precedence over the `tags` filter.","schema":{"type":"boolean"}},{"name":"startDate","required":false,"in":"query","description":"Only return threads whose `latestMessageReceivedDate` is at/after this instant (inclusive). Accepts an ISO-8601 date-time.","schema":{"format":"date-time","example":"2024-01-01T00:00:00.000Z","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"Only return threads whose `latestMessageReceivedDate` is at/before this instant (inclusive). Accepts an ISO-8601 date-time.","schema":{"format":"date-time","example":"2024-12-31T23:59:59.999Z","type":"string"}},{"name":"sourceEmails","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"latestMessageReceivedDate","type":"string","enum":["subject","latestMessageSentDate","latestMessageReceivedDate"]}},{"name":"includeLastMessage","required":false,"in":"query","description":"When truthy (1/true), populate lastMessageOrDraft (id/subject); otherwise it is returned as null. The message body is never included — use the thread `snippet` for previews, or fetch the body from the thread-emails endpoint.","schema":{"type":"boolean"}},{"name":"includeHidden","required":false,"in":"query","description":"When true, include hidden threads (hiddenAt != null). Default false: hidden threads are excluded from the listing.","schema":{"default":false,"type":"boolean"}},{"name":"sharedStatus","required":false,"in":"query","description":"Opposing-council only. Narrows to threads in one sharing state; ignored for OL/OG callers.","schema":{"type":"string","enum":["shared","pending","not_gated"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEmailDataThreadEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List email threads","tags":["local-emails"]},"delete":{"description":"Permanently deletes threads from the case, with their emails and attachments, and updates the case's data counts.","operationId":"LocalEmailMutationsController_deleteEmailThreads","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteEmailThreadsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Delete email threads","tags":["local-emails"]}},"/local-emails/{caseId}/threads/{threadId}/emails":{"get":{"description":"Returns the emails of one thread with their bodies, recipients and attachments.","operationId":"LocalEmailDataController_getThreadEmails","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"threadId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"folders","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"domains","required":false,"in":"query","description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeDomains","required":false,"in":"query","description":"Exclude emails whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeTags","required":false,"in":"query","description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeEmails","required":false,"in":"query","description":"Exclude emails whose from/to/cc/bcc address matches any of these (any-of).","schema":{"type":"array","items":{"type":"string"}}},{"name":"starred","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"unread","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"untagged","required":false,"in":"query","description":"When truthy (1/true), return only emails with no tags attached. Takes precedence over the `tags` filter.","schema":{"type":"boolean"}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"date","type":"string","enum":["date"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEmailDataEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List the emails in a thread","tags":["local-emails"]}},"/local-emails/{caseId}/emails/search":{"get":{"description":"Finds emails in a case by text, folder, domain, participant, tag or date. Bodies are not included; fetch the thread's emails for those.","operationId":"LocalEmailDataController_searchEmails","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Text search applied to all indexed fields (subject, body, snippet, from/to/cc/bcc). Supports the same AND / OR / NOT / -prefix, parentheses, \"quoted phrases\" and wildcards as the boolean query parser.","schema":{"example":"(discriminate OR racist) AND (\"Larry Graham\" OR Robinson)","type":"string"}},{"name":"folders","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"domains","required":false,"in":"query","description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive. Requires the `domains` field on the emailtext Atlas index.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeDomains","required":false,"in":"query","description":"Exclude items whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"tags","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeTags","required":false,"in":"query","description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"untagged","required":false,"in":"query","description":"When truthy (1/true), return only emails with no tags attached. Takes precedence over the `tags` filter.","schema":{"type":"boolean"}},{"name":"participants","required":false,"in":"query","description":"Filter by participant email address(es), any-of.","schema":{"type":"array","items":{"type":"string"}}},{"name":"excludeEmails","required":false,"in":"query","description":"Exclude items whose participant address matches any of these (any-of). Must not overlap `participants`.","schema":{"type":"array","items":{"type":"string"}}},{"name":"startDate","required":false,"in":"query","description":"Only return emails whose `date` is at/after this instant (inclusive). Accepts an ISO-8601 date-time.","schema":{"format":"date-time","example":"2024-01-01T00:00:00.000Z","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"Only return emails whose `date` is at/before this instant (inclusive). Accepts an ISO-8601 date-time.","schema":{"format":"date-time","example":"2024-12-31T23:59:59.999Z","type":"string"}},{"name":"sourceEmails","required":false,"in":"query","schema":{"type":"array","items":{"type":"string"}}},{"name":"sortBy","required":false,"in":"query","schema":{"default":"date","type":"string","enum":["date","subject"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UserEmailDataEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search emails","tags":["local-emails"]}},"/local-emails/{caseId}/tags-count":{"get":{"description":"Returns how many emails in the case carry each tag, for the given filters. On a case shared with opposing counsel, each side counts only its own tags.","operationId":"LocalEmailDataController_getEmailTagsCount","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"sourceEmail","required":true,"in":"query","description":"Source mailbox email to scope the counts to (the provider email).","schema":{"type":"string"}},{"name":"tagOrigin","required":false,"in":"query","description":"Filter tags by how they were created; omit for all origins.","schema":{"type":"string","enum":["manual","auto-import"]}},{"name":"sortBy","required":false,"in":"query","description":"Sort by email count or alphabetically by tag name. Default order is desc for count and asc for name; override with sortOrder.","schema":{"default":"count","type":"string","enum":["count","name"]}},{"name":"searchText","required":false,"in":"query","description":"Case-insensitive substring match on tag name.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/EmailTagsCountResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Count emails per tag","tags":["local-emails"]}},"/local-emails/{caseId}/search-counts":{"post":{"description":"For each boolean search query, and each email address, returns how many emails in the case match (only in one mailbox when providerId is given; on a case shared with opposing counsel, only in threads the caller may see). A malformed query gets an error on its own row instead of failing the whole request.","operationId":"LocalEmailDataController_getSearchCounts","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailSearchCountsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/EmailSearchCountsResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Count matches for several searches","tags":["local-emails"]}},"/local-emails/{caseId}/domains":{"get":{"description":"Lists the email domains that appear in the case's emails, with how many emails each appears in.","operationId":"LocalEmailDataController_getEmailDomains","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"sourceEmails","required":false,"in":"query","description":"Restrict to these source mailboxes (provider emails). Omit to cover every mailbox in the case.","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/EmailDomainsResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List sender and recipient domains","tags":["local-emails"]}},"/local-emails/{caseId}/domains/{domain}/addresses":{"get":{"description":"Lists the email addresses of one domain that appear in the case's emails, with how many emails each appears in.","operationId":"LocalEmailDataController_getEmailDomainAddresses","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"domain","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sourceEmails","required":false,"in":"query","description":"Restrict to these source mailboxes (provider emails). Omit to cover every mailbox in the case.","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/EmailDomainAddressesResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List addresses in a domain","tags":["local-emails"]}},"/local-emails/{caseId}/emails/{emailId}/thread":{"get":{"description":"Returns the thread an email belongs to and the ids of all its emails, newest first. An email the caller may not see is reported as not found.","operationId":"LocalEmailDataController_getThreadByEmailId","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"emailId","required":true,"in":"path","schema":{"type":"string"}},{"name":"includeLastMessage","required":false,"in":"query","description":"When truthy (1/true), populate lastMessageOrDraft (id/subject) on the thread; otherwise it is returned as null. The message body is never included — fetch it from the thread-emails endpoint.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ThreadByEmailResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the thread of an email","tags":["local-emails"]}},"/local-emails/{caseId}/emails/tags":{"post":{"description":"Adds tags to emails chosen by id, or to every email matching a search. By id, the tags are added right away; by search, a background job is queued and its id returned (see bulk-tag-jobs).","operationId":"LocalEmailMutationsController_bulkAddEmailTags","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkTagEmailsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BulkTagResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag many emails","tags":["local-emails"]}},"/local-emails/{caseId}/threads/tags":{"post":{"description":"Adds tags to threads chosen by id, or to every thread matching a search, optionally tagging each thread's emails too. By search, a background job is queued and its id returned.","operationId":"LocalEmailMutationsController_bulkAddThreadTags","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkTagThreadsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BulkTagResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag many threads","tags":["local-emails"]}},"/local-emails/{caseId}/threads/visibility":{"post":{"description":"Hides threads from the thread list, or brings hidden threads back. Nothing is deleted.","operationId":"LocalEmailMutationsController_setThreadVisibility","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkThreadVisibilityDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BulkThreadVisibilityResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Hide or unhide threads","tags":["local-emails"]}},"/local-emails/{caseId}/bulk-tag-jobs":{"get":{"description":"Lists the background tagging jobs of a case (the ones started by tagging a search), newest first by default.","operationId":"LocalEmailMutationsController_listBulkTagJobs","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["queued","in_progress","completed","failed"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BulkTagJobEntity"}},"total":{"type":"number","example":3},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List bulk-tag jobs","tags":["local-emails"]}},"/local-emails/{caseId}/bulk-tag-jobs/{jobId}":{"get":{"description":"Returns one background tagging job with its status and progress.","operationId":"LocalEmailMutationsController_getBulkTagJob","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BulkTagJobEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a bulk-tag job","tags":["local-emails"]}},"/local-emails/{userEmailDataId}/tags":{"patch":{"description":"Replaces the email's tags with the given list (an empty list removes them) and returns the email. On a case shared with opposing counsel, the response shows only the caller's own tags.","operationId":"LocalEmailMutationsController_updateUserEmailDataTags","parameters":[{"name":"userEmailDataId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserEmailDataTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEmailDataEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Set an email's tags","tags":["local-emails"]}},"/local-emails/{caseId}/thread/{threadId}/tags":{"patch":{"description":"Replaces the thread's tags with the given list and returns the thread. On a case shared with opposing counsel, the response shows only the caller's own tags.","operationId":"LocalEmailMutationsController_updateEmailThreadTags","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"threadId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEmailThreadTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserEmailDataThreadEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Set a thread's tags","tags":["local-emails"]}},"/discord/{caseId}":{"get":{"description":"Returns one page of Discord profiles linked to the case, excluding archived ones; search by username, global name or email, or filter by extraction code. On a case shared with opposing counsel, profiles the caller may not see are excluded; ocSharedStatus (the profile's sharing status) is filled in for opposing counsel, who can also filter by it, and is null for everyone else.","operationId":"DiscordDataController_searchProfiles","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"extractionCode","required":false,"in":"query","description":"Filter by extraction code","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","description":"Search username, globalName, email","schema":{"type":"string"}},{"name":"sharedStatus","required":false,"in":"query","description":"Opposing-council only. Narrows to providers in one sharing state; ignored for OL/OG callers.","schema":{"type":"string","enum":["shared","pending","not_gated"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DiscordUserProviderEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Discord profiles","tags":["discord"]},"delete":{"description":"Archives (soft-deletes) a Discord profile linked to the case so it no longer appears in listings, and resets the case's Discord data source status to pending. Returns 404 if the profile is not linked or already archived. Not available to opposing counsel.","operationId":"DiscordDataController_archiveDiscordProfile","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteDiscordProfileDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Archive a Discord profile","tags":["discord"]}},"/discord/{caseId}/profile/{discordId}":{"get":{"description":"Returns one Discord profile linked to the case; returns 404 if it is archived or not linked. On a case shared with opposing counsel, a profile the caller may not see returns 404.","operationId":"DiscordDataController_getProfile","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"discordId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DiscordUserProviderEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a Discord profile","tags":["discord"]}},"/discord/{caseId}/profile/{discordId}/conversations":{"get":{"description":"Returns one page of Discord conversations for a profile in the case, with message counts and last-message details; search by name, display name or channel id. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"DiscordDataController_searchConversations","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"discordId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search name, displayName, channelId","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DiscordConversationEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Discord conversations","tags":["discord"]}},"/discord/{caseId}/profile/{discordId}/conversation/{conversationId}/messages":{"get":{"description":"Returns one page of messages in a Discord conversation for a profile, sorted by date; searchText filters by message text. On a case shared with opposing counsel, a profile the caller may not see returns an empty page. Returns 404 when the profile is not linked to the case.","operationId":"DiscordDataController_searchMessages","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"discordId","required":true,"in":"path","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search message text","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DiscordMessageEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Discord messages","tags":["discord"]}},"/discord/{caseId}/import":{"get":{"description":"Returns one page of Discord import jobs for the case, optionally filtered by status.","operationId":"DiscordDataController_searchImportJobs","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"status","required":false,"in":"query","description":"Filter by job status","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DiscordImportJobEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Discord import jobs","tags":["discord"]},"post":{"description":"Creates an import job for an uploaded Discord data export file, marks the case's Discord data source as pending, and queues the import. Returns 400 if the file was already imported successfully, and 404 if the file or extraction code is not found. Not available to opposing counsel.","operationId":"DiscordDataController_importDiscordData","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportDiscordDataDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DiscordImportJobEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a Discord import (internal)","tags":["discord"],"x-internal":true}},"/discord/{caseId}/import/{importId}":{"get":{"description":"Returns one Discord import job for the case, including its status and error message. Returns 404 if the job does not belong to the case.","operationId":"DiscordDataController_getImportJob","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"importId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DiscordImportJobEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a Discord import job","tags":["discord"]}},"/discord/{caseId}/profile/{discordId}/activity":{"get":{"description":"Returns one page of activity events parsed from the Activity folder of a profile's Discord export; filter by event source, or search by event type. On a case shared with opposing counsel, a profile the caller may not see returns an empty page. Returns 404 when the profile is not linked to the case.","operationId":"DiscordDataController_searchActivities","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"discordId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search eventType","schema":{"type":"string"}},{"name":"eventSource","required":false,"in":"query","description":"Filter by event source (analytics, modeling, reporting, tns)","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DiscordActivityEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Discord activity events","tags":["discord"]}},"/discord/{caseId}/profile/{discordId}/support-tickets":{"get":{"description":"Returns one page of Discord support tickets for a profile in the case, with their comments; search by subject or comment text. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"DiscordDataController_searchSupportTickets","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"discordId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search subject, comments.comment","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DiscordSupportTicketEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Discord support tickets","tags":["discord"]}},"/reddit/{caseId}":{"get":{"description":"Returns one page of Reddit profiles linked to the case, excluding archived ones; search by Reddit user id or email, or filter by extraction code. On a case shared with opposing counsel, profiles the caller may not see are excluded; ocSharedStatus (the profile's sharing status) is filled in for opposing counsel, who can also filter by it, and is null for everyone else.","operationId":"RedditDataController_searchProfiles","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"extractionCode","required":false,"in":"query","description":"Filter by extraction code","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","description":"Search username, email","schema":{"type":"string"}},{"name":"sharedStatus","required":false,"in":"query","description":"Opposing-council only. Narrows to providers in one sharing state; ignored for OL/OG callers.","schema":{"type":"string","enum":["shared","pending","not_gated"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RedditUserProviderEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Reddit profiles","tags":["reddit"]},"delete":{"description":"Archives (soft-deletes) a Reddit profile linked to the case so it no longer appears in listings, and resets the case's Reddit data source status to pending. Returns 404 if the profile is not linked or already archived. Not available to opposing counsel.","operationId":"RedditDataController_archiveRedditProfile","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteRedditProfileDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Archive a Reddit profile","tags":["reddit"]}},"/reddit/{caseId}/profile/{redditId}":{"get":{"description":"Returns one Reddit profile linked to the case; returns 404 if it is archived or not linked. On a case shared with opposing counsel, a profile the caller may not see returns 404.","operationId":"RedditDataController_getProfile","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"redditId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RedditUserProviderEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a Reddit profile","tags":["reddit"]}},"/reddit/{caseId}/profile/{redditId}/posts":{"get":{"description":"Returns one page of posts made by a Reddit profile in the case, sorted by date; search by title, body or subreddit. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"RedditDataController_searchPosts","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"redditId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search title, body, subreddit","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RedditPostEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Reddit posts","tags":["reddit"]}},"/reddit/{caseId}/profile/{redditId}/comments":{"get":{"description":"Returns one page of comments made by a Reddit profile in the case, sorted by date; search by body or subreddit. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"RedditDataController_searchComments","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"redditId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search body, subreddit","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RedditCommentEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Reddit comments","tags":["reddit"]}},"/reddit/{caseId}/profile/{redditId}/messages":{"get":{"description":"Returns one page of private messages for a Reddit profile in the case, sorted by date; search by subject, body, sender or recipient. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"RedditDataController_searchMessages","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"redditId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search subject, body, from, to","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RedditMessageEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Reddit private messages","tags":["reddit"]}},"/reddit/{caseId}/profile/{redditId}/chat-messages":{"get":{"description":"Returns one page of Reddit chat messages for a profile in the case, sorted by when they were sent; search by message text or username. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"RedditDataController_searchChatMessages","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"redditId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search message, username","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RedditChatMessageEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Reddit chat messages","tags":["reddit"]}},"/reddit/{caseId}/profile/{redditId}/votes":{"get":{"description":"Returns one page of upvotes and downvotes cast by a Reddit profile; filter by vote type (post or comment). On a case shared with opposing counsel, a profile the caller may not see returns an empty page. Returns 404 when the profile is not linked to the case.","operationId":"RedditDataController_searchVotes","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"redditId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"voteType","required":false,"in":"query","description":"Filter by vote type: comment or post","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RedditVoteEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Reddit votes","tags":["reddit"]}},"/reddit/{caseId}/import":{"get":{"description":"Returns one page of Reddit import jobs for the case, optionally filtered by status.","operationId":"RedditDataController_searchImportJobs","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"status","required":false,"in":"query","description":"Filter by job status","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RedditImportJobEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List Reddit import jobs","tags":["reddit"]},"post":{"description":"Creates an import job for an uploaded Reddit data export file, marks the case's Reddit data source as pending, and queues the import. Returns 400 if the file was already imported successfully, and 404 if the file or extraction code is not found. Not available to opposing counsel.","operationId":"RedditDataController_importRedditData","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportRedditDataDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RedditImportJobEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a Reddit import (internal)","tags":["reddit"],"x-internal":true}},"/reddit/{caseId}/import/{importId}":{"get":{"description":"Returns one Reddit import job for the case, including its status and error message. Returns 404 if the job does not belong to the case.","operationId":"RedditDataController_getImportJob","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"importId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RedditImportJobEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a Reddit import job","tags":["reddit"]}},"/linkedin/{caseId}":{"get":{"description":"Returns one page of LinkedIn profiles linked to the case, excluding archived ones; search by first name, last name, email or headline, or filter by extraction code. On a case shared with opposing counsel, profiles the caller may not see are excluded; ocSharedStatus (the profile's sharing status) is filled in for opposing counsel, who can also filter by it, and is null for everyone else.","operationId":"LinkedInDataController_searchProfiles","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search firstName, lastName, email, headline","schema":{"type":"string"}},{"name":"extractionCode","required":false,"in":"query","description":"Filter by extraction code","schema":{"type":"string"}},{"name":"sharedStatus","required":false,"in":"query","description":"Opposing-council only. Narrows to providers in one sharing state; ignored for OL/OG callers.","schema":{"type":"string","enum":["shared","pending","not_gated"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInUserProviderEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn profiles","tags":["linkedin"]}},"/linkedin/{caseId}/import-jobs":{"get":{"description":"Returns one page of LinkedIn import jobs for the case, optionally filtered by status.","operationId":"LinkedInDataController_searchImportJobs","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"status","required":false,"in":"query","description":"Filter by job status","schema":{"type":"string","enum":["not_started","in_progress","completed","failed"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInImportJobEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn import jobs","tags":["linkedin"]}},"/linkedin/{caseId}/import-jobs/coverage":{"get":{"description":"Extraction plumbing: reports which data types and conversations of an uploaded LinkedIn export file have already been imported into this case, combined across its completed import jobs. Returns 404 if the file is not found.","operationId":"LinkedInDataController_getImportCoverage","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"fileId","required":true,"in":"query","description":"UserFile ID of the uploaded zip to report coverage for","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LinkedInImportCoverageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get LinkedIn import coverage for a file (internal)","tags":["linkedin"],"x-internal":true}},"/linkedin/{caseId}/import-jobs/{importJobId}":{"get":{"description":"Returns one LinkedIn import job for the case, including its status, selected data types and error message. Returns 404 if the job does not belong to the case.","operationId":"LinkedInDataController_getImportJob","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"importJobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LinkedInImportJobEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a LinkedIn import job","tags":["linkedin"]}},"/linkedin/{caseId}/import":{"post":{"description":"Creates an import job for an uploaded LinkedIn data export file (optionally limited to some data types or conversations), marks the case's LinkedIn data source as pending, and queues the import (201). If the file was already imported into this case, the request either fails with 400 or, when incremental re-import is enabled and it would add nothing new, returns 200 with imported set to false and no job created. Not available to opposing counsel.","operationId":"LinkedInDataController_importLinkedInData","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportLinkedInDataDto"}}}},"responses":{"200":{"description":"The selected data has already been imported; no job was created","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LinkedInImportSkippedResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}},"201":{"description":"Import job created and queued","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LinkedInImportJobEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start a LinkedIn import (internal)","tags":["linkedin"],"x-internal":true}},"/linkedin/{caseId}/{linkedInId}/export":{"post":{"description":"Creates a PDF export job for one type of LinkedIn data (conversations, connections, job applications and so on) from a profile in the case, queues it, and returns the job id; poll the export status route for the result. Returns 404 if the profile is not linked to the case. On a case shared with opposing counsel, a profile the caller may not see also returns 404.","operationId":"LinkedInDataController_exportLinkedInData","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportLinkedInDataDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Request a LinkedIn PDF export","tags":["linkedin"]}},"/linkedin/{caseId}/exports":{"get":{"description":"Returns every LinkedIn export record for the case, newest first, with the generated file (once finished) and the requesting user. On a case shared with opposing counsel, exports of a profile the caller may not see are excluded.","operationId":"LinkedInDataController_listExports","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"type":"object","description":"A stored LinkedIn export record. Every stored field of the record is returned; the common ones are listed here.","properties":{"source":{"type":"string","example":"linkedin"},"type":{"type":"string","example":"pdf"},"status":{"type":"string","enum":["not-started","inprogress","completed","failed","cancelled"]},"sortBy":{"type":"string","enum":["old-to-new","new-to-old"]},"exportConfig":{"type":"array","items":{"type":"string"}},"metadata":{"type":"object","properties":{"linkedInProviderId":{"type":"string"},"linkedInExportType":{"type":"string","enum":["conversations","connections","job-applications","rich-media","saved-jobs","saved-job-alerts","saved-items"]},"custodianName":{"type":"string","nullable":true},"conversationIds":{"type":"array","items":{"type":"string"},"nullable":true}}},"file":{"type":"object","nullable":true,"description":"The generated PDF once the export has finished (public file fields only).","properties":{"name":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"}}},"exportedBy":{"type":"object","description":"The user who requested the export.","properties":{"name":{"type":"string"},"email":{"type":"string"}}},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn exports","tags":["linkedin"]}},"/linkedin/{caseId}/exports/{jobId}":{"get":{"description":"Returns one LinkedIn export record for the case with its status, the generated file once finished, and the requesting user. Returns 404 if the export does not belong to the case. On a case shared with opposing counsel, an export of a profile the caller may not see also returns 404.","operationId":"LinkedInDataController_getExportStatus","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"A stored LinkedIn export record. Every stored field of the record is returned; the common ones are listed here.","properties":{"source":{"type":"string","example":"linkedin"},"type":{"type":"string","example":"pdf"},"status":{"type":"string","enum":["not-started","inprogress","completed","failed","cancelled"]},"sortBy":{"type":"string","enum":["old-to-new","new-to-old"]},"exportConfig":{"type":"array","items":{"type":"string"}},"metadata":{"type":"object","properties":{"linkedInProviderId":{"type":"string"},"linkedInExportType":{"type":"string","enum":["conversations","connections","job-applications","rich-media","saved-jobs","saved-job-alerts","saved-items"]},"custodianName":{"type":"string","nullable":true},"conversationIds":{"type":"array","items":{"type":"string"},"nullable":true}}},"file":{"type":"object","nullable":true,"description":"The generated PDF once the export has finished (public file fields only).","properties":{"name":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"}}},"exportedBy":{"type":"object","description":"The user who requested the export.","properties":{"name":{"type":"string"},"email":{"type":"string"}}},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a LinkedIn export","tags":["linkedin"]}},"/linkedin/{caseId}/search-messages":{"get":{"description":"Searches message text and subjects across every LinkedIn conversation in the case and returns one page of matches, newest first, each with its conversation title and participant names; optionally limit to conversations carrying all of the given tags. On a case shared with opposing counsel, messages from profiles the caller may not see are excluded.","operationId":"LinkedInDataController_searchMessageContent","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":true,"in":"query","description":"Search text for message content and subject","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Filter by conversation tag IDs (comma-separated)","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInMessageSearchResultEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search LinkedIn messages in a case","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}":{"get":{"description":"Returns one LinkedIn profile linked to the case; returns 404 if it is archived or not linked. On a case shared with opposing counsel, a profile the caller may not see returns 404.","operationId":"LinkedInDataController_getProfile","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LinkedInUserProviderEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a LinkedIn profile","tags":["linkedin"]},"delete":{"description":"With action archive, soft-deletes the LinkedIn profile so it no longer appears in listings; with action delete, permanently deletes the profile and all of its imported LinkedIn data (conversations, messages, connections and the rest). Either way the case's LinkedIn data source status is reset to pending. Returns 404 if the profile is not linked to the case. Not available to opposing counsel.","operationId":"LinkedInDataController_archiveOrDeleteProfile","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteLinkedInProfileDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Archive or delete a LinkedIn profile","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/conversations":{"get":{"description":"Returns one page of LinkedIn message conversations for a profile in the case, with participants and attachedTags; search by title or participant name, or filter to conversations carrying all of the given tags. On a case shared with opposing counsel, a profile the caller may not see returns an empty page. attachedTags leave out tags the other side added by hand.","operationId":"LinkedInDataController_searchConversations","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search title, participantNames","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Filter by tag IDs","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInConversationEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn conversations","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/conversations/{conversationId}":{"get":{"description":"Returns one LinkedIn conversation for a profile in the case, with participants and attachedTags. Returns 404 if the conversation does not belong to the profile and case. On a case shared with opposing counsel, a profile the caller may not see returns 404. attachedTags leave out tags the other side added by hand.","operationId":"LinkedInDataController_getConversation","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LinkedInConversationEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a LinkedIn conversation","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/conversations/{conversationId}/messages":{"get":{"description":"Returns one page of messages in a LinkedIn conversation, sorted by date; searchText filters by message text. Returns 404 if the conversation does not belong to the profile and case. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"LinkedInDataController_searchMessages","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search message contentText","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInMessageEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn messages","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/connections":{"get":{"description":"Returns one page of a profile's LinkedIn connections in the case, with attachedTags; search by name, company or position, or filter to connections carrying all of the given tags. On a case shared with opposing counsel, a profile the caller may not see returns an empty page. attachedTags leave out tags the other side added by hand.","operationId":"LinkedInDataController_searchConnections","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search firstName, lastName, company, position","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Filter by tag IDs","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInConnectionEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn connections","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/comments":{"get":{"description":"Returns one page of LinkedIn comments from the profile's export, sorted by date; search by comment text or post link, or filter by tags. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"LinkedInDataController_searchComments","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search message, postLink","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Filter by tag IDs","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInCommentEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn comments","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/rich-media":{"get":{"description":"Returns one page of rich media entries (media type, link and description) from the profile's LinkedIn export, sorted by date; search by description, or filter by tags. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"LinkedInDataController_searchRichMedia","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search description","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Filter by tag IDs","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInRichMediaEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn rich media","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/saved-jobs":{"get":{"description":"Returns one page of jobs the profile saved on LinkedIn, sorted by saved date; search by job title or company, or filter by tags. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"LinkedInDataController_searchSavedJobs","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search jobTitle, companyName","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Filter by tag IDs","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInSavedJobEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn saved jobs","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/saved-items":{"get":{"description":"Returns one page of items the profile saved on LinkedIn, sorted by when they were saved; search by URL. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"LinkedInDataController_searchSavedItems","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search url","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInSavedItemEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn saved items","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/job-applications":{"get":{"description":"Returns one page of the profile's LinkedIn job applications, sorted by application date; search by job title or company, or filter by tags. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"LinkedInDataController_searchJobApplications","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search jobTitle, companyName","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Filter by tag IDs","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInJobApplicationEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn job applications","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/saved-job-alerts":{"get":{"description":"Returns all of the profile's saved LinkedIn job alerts in the case, newest first, in a single unpaginated list. On a case shared with opposing counsel, a profile the caller may not see returns an empty list.","operationId":"LinkedInDataController_searchSavedJobAlerts","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInSavedJobAlertEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List LinkedIn saved job alerts","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/conversations/{conversationId}/tags":{"patch":{"description":"Adds the given tag to a LinkedIn conversation, or removes it if it is already applied, and returns the updated conversation. Returns 404 if the conversation does not belong to the profile and case. In the result, attachedTags leave out tags the other side added by hand.","operationId":"LinkedInDataController_updateConversationTags","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLinkedInTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LinkedInConversationEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Toggle a tag on a LinkedIn conversation","tags":["linkedin"]}},"/linkedin/{caseId}/{linkedInId}/connections/{connectionId}/tags":{"patch":{"description":"Adds the given tag to a LinkedIn connection, or removes it if it is already applied, and returns the updated connection. Returns 404 if the connection does not belong to the profile and case. In the result, attachedTags leave out tags the other side added by hand.","operationId":"LinkedInDataController_updateConnectionTags","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"linkedInId","required":true,"in":"path","schema":{"type":"string"}},{"name":"connectionId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLinkedInTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LinkedInConnectionEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Toggle a tag on a LinkedIn connection","tags":["linkedin"]}},"/ai-chat/{caseId}":{"get":{"description":"Returns one page of AI chat profiles (ChatGPT, Claude or Gemini accounts) linked to the case, excluding archived ones; search by display name, email or account id, or filter by extraction code. On a case shared with opposing counsel, profiles the caller may not see are excluded; ocSharedStatus (the profile's sharing status) is filled in for opposing counsel, who can also filter by it, and is null for everyone else.","operationId":"AiChatDataController_searchProfiles","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"extractionCode","required":false,"in":"query","description":"Filter by extraction code","schema":{"type":"string"}},{"name":"searchText","required":false,"in":"query","description":"Search displayName, email","schema":{"type":"string"}},{"name":"sharedStatus","required":false,"in":"query","description":"Opposing-council only. Narrows to providers in one sharing state; ignored for OL/OG callers.","schema":{"type":"string","enum":["shared","pending","not_gated"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AiChatProviderEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List AI chat profiles","tags":["ai-chat"]},"delete":{"description":"Archives (soft-deletes) an AI chat profile linked to the case so it no longer appears in listings, and resets the case's data source status for that platform to pending. Returns 404 if the profile is not linked or already archived. Not available to opposing counsel.","operationId":"AiChatDataController_archiveProfile","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAiChatProfileDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/MessageResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Archive an AI chat profile","tags":["ai-chat"]}},"/ai-chat/{caseId}/search-messages":{"get":{"description":"Searches message text across every AI chat conversation in the case and returns one page of matches, each with its conversation title and a highlighted copy of the text; optionally limit to conversations carrying all of the given tags. Archived profiles are skipped. On a case shared with opposing counsel, messages from profiles the caller may not see are excluded.","operationId":"AiChatDataController_searchMessageContent","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":true,"in":"query","description":"Search text for message content","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Filter by conversation tag IDs (comma-separated)","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AiChatMessageSearchResultEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search AI chat messages in a case","tags":["ai-chat"]}},"/ai-chat/{caseId}/profile/{providerId}":{"get":{"description":"Returns one AI chat profile linked to the case; returns 404 if it is archived or not linked. On a case shared with opposing counsel, a profile the caller may not see returns 404.","operationId":"AiChatDataController_getProfile","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AiChatProviderEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an AI chat profile","tags":["ai-chat"]}},"/ai-chat/{caseId}/profile/{providerId}/conversations":{"get":{"description":"Returns one page of conversations for an AI chat profile in the case, with tag names and attachedTags; search by title or conversation id, or filter to conversations carrying all of the given tags. On a case shared with opposing counsel, a profile the caller may not see returns an empty page. attachedTags leave out tags the other side added by hand.","operationId":"AiChatDataController_searchConversations","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search conversation title or content","schema":{"type":"string"}},{"name":"tags","required":false,"in":"query","description":"Filter by tag IDs","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AiChatConversationEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List AI chat conversations","tags":["ai-chat"]}},"/ai-chat/{caseId}/profile/{providerId}/conversation/{conversationId}/messages":{"get":{"description":"Returns one page of messages in an AI chat conversation, including tool uses and attachments; when searchText is given, only matching messages are returned, with highlightedText marking the matches. Returns 404 if the conversation does not belong to the profile and case. On a case shared with opposing counsel, a profile the caller may not see returns an empty page.","operationId":"AiChatDataController_searchMessages","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"searchText","required":false,"in":"query","description":"Search message text","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AiChatMessageEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List AI chat messages","tags":["ai-chat"]}},"/ai-chat/{caseId}/profile/{providerId}/conversation/{conversationId}/export-pdf":{"post":{"description":"Renders the whole conversation to a PDF while the request waits, stores it, and returns the stored file record; export started, finished or failed events are recorded on the case. Returns 404 if the conversation is not linked to the case.","operationId":"AiChatDataController_exportConversationToPdf","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UserFileResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export an AI chat conversation to PDF","tags":["ai-chat"]}},"/ai-chat/{caseId}/import":{"get":{"description":"Returns one page of AI chat import jobs for the case, newest first by default, optionally filtered by status.","operationId":"AiChatDataController_searchImportJobs","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"status","required":false,"in":"query","description":"Filter by job status","schema":{"type":"string","enum":["not_started","in_progress","completed","failed"]}},{"name":"searchText","required":false,"in":"query","description":"Search import jobs","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AiChatImportJobEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List AI chat import jobs","tags":["ai-chat"]},"post":{"description":"Desktop plumbing: creates an import job for an uploaded ChatGPT, Claude or Gemini export file, marks the case's AI chat data source as pending, and queues the import. Returns 400 if the file was already imported successfully, and 404 if the file or extraction code is not found. Not available to opposing counsel.","operationId":"AiChatDataController_importAiChatData","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportAiChatDataDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AiChatImportJobEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Start an AI chat import (internal)","tags":["ai-chat"],"x-internal":true}},"/ai-chat/{caseId}/import/{importId}":{"get":{"description":"Desktop plumbing: returns one AI chat import job for the case, including its status, error message and import counts, so the uploader can poll progress. Returns 404 if the job does not belong to the case.","operationId":"AiChatDataController_getImportJob","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"importId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AiChatImportJobEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get an AI chat import job (internal)","tags":["ai-chat"],"x-internal":true}},"/ai-chat/{caseId}/profile/{providerId}/conversation/{conversationId}/tags":{"patch":{"description":"Replaces an AI chat conversation's tags with the given list of tag ids or names; names that do not exist yet are created as case tags. Returns the updated conversation, where attachedTags leave out tags the other side added by hand.","operationId":"AiChatDataController_updateConversationTags","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAiChatConversationTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AiChatConversationEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Set tags on an AI chat conversation","tags":["ai-chat"]}},"/ai/admin/ping/embedding":{"post":{"description":"Embeds a fixed test string through Bedrock and reports the model, latency and, in `sample`, the vector size and its first three values. A Bedrock failure still answers 200, with `ok: false`, `model: unknown` and the error class and message. Super admins only.","operationId":"AiAdminController_pingEmbedding","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"purpose":{"type":"string","example":"admin_ping"},"model":{"type":"string","description":"unknown when the call failed"},"latencyMs":{"type":"number","example":412},"sample":{"type":"string","description":"Present when ok is true."},"errorClass":{"type":"string","description":"Present when ok is false."},"errorMessage":{"type":"string","description":"Present when ok is false."}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Smoke-test Bedrock embeddings (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/ping/claude":{"post":{"description":"Sends a fixed ping prompt to the Claude haiku model through Bedrock and reports the model, latency and, in `sample`, the first 64 characters of the reply. A Bedrock failure still answers 200, with `ok: false`, `model: unknown` and the error class and message. Super admins only.","operationId":"AiAdminController_pingClaude","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"purpose":{"type":"string","example":"admin_ping"},"model":{"type":"string","description":"unknown when the call failed"},"latencyMs":{"type":"number","example":412},"sample":{"type":"string","description":"Present when ok is true."},"errorClass":{"type":"string","description":"Present when ok is false."},"errorMessage":{"type":"string","description":"Present when ok is false."}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Smoke-test Bedrock Claude (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/ping/rerank":{"post":{"description":"Reranks three fixed documents against a fixed query through Bedrock and reports the model, latency and, in `sample`, each result index with its score. A Bedrock failure still answers 200, with `ok: false`, `model: unknown` and the error class and message. Super admins only.","operationId":"AiAdminController_pingRerank","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"purpose":{"type":"string","example":"admin_ping"},"model":{"type":"string","description":"unknown when the call failed"},"latencyMs":{"type":"number","example":412},"sample":{"type":"string","description":"Present when ok is true."},"errorClass":{"type":"string","description":"Present when ok is false."},"errorMessage":{"type":"string","description":"Present when ok is false."}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Smoke-test Bedrock rerank (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/backfill/device-messages":{"post":{"description":"Queues AI ingestion of device messages for the cases in `caseIds`, else every case of the organizations in `organizationIds`, else every active case when `confirmUnrestricted` is true (otherwise 500); an unrestricted run over more than 5 million items also needs `force`. Archived cases are skipped. `dryRun` only counts; `force` first clears existing embeddings in scope so they are rebuilt, even on a dry run. Super admins only.","operationId":"AiAdminController_backfillDeviceMessages","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiBackfillRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"casesScanned":{"type":"number","example":12},"casesEnqueued":{"type":"number","example":12,"description":"0 on a dry run."},"estimatedMessageScope":{"type":"number","example":48000},"forced":{"type":"boolean","example":false},"dryRun":{"type":"boolean","example":false}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Backfill AI ingestion for device messages (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/backfill/linkedin":{"post":{"description":"Queues AI ingestion of LinkedIn data for the cases in `caseIds`, else every case of the organizations in `organizationIds`, else every active case when `confirmUnrestricted` is true (otherwise 500); an unrestricted run over more than 5 million items also needs `force`. Archived cases are skipped. `dryRun` only counts; `force` first clears existing embeddings in scope so they are rebuilt, even on a dry run. Super admins only.","operationId":"AiAdminController_backfillLinkedIn","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiBackfillRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"casesScanned":{"type":"number","example":12},"casesEnqueued":{"type":"number","example":12,"description":"0 on a dry run."},"estimatedMessageScope":{"type":"number","example":48000},"forced":{"type":"boolean","example":false},"dryRun":{"type":"boolean","example":false}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Backfill AI ingestion for LinkedIn (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/backfill/discord":{"post":{"description":"Queues AI ingestion of Discord data for the cases in `caseIds`, else every case of the organizations in `organizationIds`, else every active case when `confirmUnrestricted` is true (otherwise 500); an unrestricted run over more than 5 million items also needs `force`. Archived cases are skipped. `dryRun` only counts; `force` first clears existing embeddings in scope so they are rebuilt, even on a dry run. Super admins only.","operationId":"AiAdminController_backfillDiscord","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiBackfillRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"casesScanned":{"type":"number","example":12},"casesEnqueued":{"type":"number","example":12,"description":"0 on a dry run."},"estimatedMessageScope":{"type":"number","example":48000},"forced":{"type":"boolean","example":false},"dryRun":{"type":"boolean","example":false}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Backfill AI ingestion for Discord (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/backfill/email":{"post":{"description":"Queues AI ingestion of email for the cases in `caseIds`, else every case of the organizations in `organizationIds`, else every active case when `confirmUnrestricted` is true (otherwise 500); an unrestricted run over more than 5 million items also needs `force`. Archived cases are skipped. `dryRun` only counts; `force` first clears existing embeddings in scope so they are rebuilt, even on a dry run. Super admins only.","operationId":"AiAdminController_backfillEmail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiBackfillRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"casesScanned":{"type":"number","example":12},"casesEnqueued":{"type":"number","example":12,"description":"0 on a dry run."},"estimatedMessageScope":{"type":"number","example":48000},"forced":{"type":"boolean","example":false},"dryRun":{"type":"boolean","example":false}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Backfill AI ingestion for email (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/backfill/aichat":{"post":{"description":"Queues AI ingestion of AI chat data for the cases in `caseIds`, else every case of the organizations in `organizationIds`, else every active case when `confirmUnrestricted` is true (otherwise 500); an unrestricted run over more than 5 million items also needs `force`. Archived cases are skipped. `dryRun` only counts; `force` first clears existing embeddings in scope so they are rebuilt, even on a dry run. Super admins only.","operationId":"AiAdminController_backfillAiChat","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiBackfillRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"casesScanned":{"type":"number","example":12},"casesEnqueued":{"type":"number","example":12,"description":"0 on a dry run."},"estimatedMessageScope":{"type":"number","example":48000},"forced":{"type":"boolean","example":false},"dryRun":{"type":"boolean","example":false}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Backfill AI ingestion for AI chat (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/backfill/reddit":{"post":{"description":"Queues AI ingestion of Reddit data for the cases in `caseIds`, else every case of the organizations in `organizationIds`, else every active case when `confirmUnrestricted` is true (otherwise 500); an unrestricted run over more than 5 million items also needs `force`. Archived cases are skipped. `dryRun` only counts; `force` first clears existing embeddings in scope so they are rebuilt, even on a dry run. Super admins only.","operationId":"AiAdminController_backfillReddit","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiBackfillRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"casesScanned":{"type":"number","example":12},"casesEnqueued":{"type":"number","example":12,"description":"0 on a dry run."},"estimatedMessageScope":{"type":"number","example":48000},"forced":{"type":"boolean","example":false},"dryRun":{"type":"boolean","example":false}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Backfill AI ingestion for Reddit (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/cases/{caseId}/jobs/cancel":{"post":{"description":"Removes the waiting, delayed, paused and active AI jobs of the case from every AI queue and reports how many were removed per queue and state. A job already running may still finish its current step. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseResetController_cancelJobs","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"cancelled":{"type":"object","description":"Jobs removed per AI queue, by job state.","additionalProperties":{"type":"object","properties":{"waiting":{"type":"number"},"delayed":{"type":"number"},"paused":{"type":"number"},"active":{"type":"number"}}},"example":{"ai-embedding-jobs":{"waiting":4,"delayed":0,"paused":0,"active":1}}},"totalCancelled":{"type":"number","example":5}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Cancel the AI jobs of a case","tags":["ai"]}},"/ai/cases/{caseId}/reset":{"post":{"description":"Cancels the case AI jobs, then deletes its AI pipeline data (chunks, summaries, snippets, processing state, tag suggestion cache, filing extractions and message embeddings) while keeping attorney work: attorney-stated and confirmed context fields, attorney notes and evidentiary map overrides. With `dryRun: true` it only counts what would be removed. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseResetController_resetCase","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCaseResetDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"dryRun":{"type":"boolean","example":false},"cancelledJobs":{"type":"object","properties":{"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"cancelled":{"type":"object","description":"Jobs removed per AI queue, by job state.","additionalProperties":{"type":"object","properties":{"waiting":{"type":"number"},"delayed":{"type":"number"},"paused":{"type":"number"},"active":{"type":"number"}}},"example":{"ai-embedding-jobs":{"waiting":4,"delayed":0,"paused":0,"active":1}}},"totalCancelled":{"type":"number","example":5}}},"deleted":{"type":"object","description":"Documents deleted per collection (counted only on a dry run).","properties":{"chunks":{"type":"number"},"periodSummaries":{"type":"number"},"conversationSummaries":{"type":"number"},"evidenceSnippets":{"type":"number"},"processingStates":{"type":"number"},"tagSuggestionCacheEntries":{"type":"number"},"contextExtractions":{"type":"number"}}},"messagesEmbeddingsCleared":{"type":"number","example":1200},"caseSummaryReset":{"type":"boolean","example":true},"evidentiaryMapEntriesPreserved":{"type":"number","example":2},"contextFieldsCleared":{"type":"array","items":{"type":"string"},"example":["keyDates"]},"preserved":{"type":"array","items":{"type":"string"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Reset the AI data of a case","tags":["ai"]}},"/ai/cases/{caseId}/search":{"post":{"description":"Runs a semantic search over the case evidence and returns the best matches (messages or conversation chunks) with score, snippet and, when asked, the surrounding messages, filtered by source, dates, participants, conversations, topics or relevance. On a case shared with opposing counsel, callers on the other side see only evidence released to them, and conversation chunks are left out. Requires commenter access or above to the case and an active plan (403 otherwise).","operationId":"AiSearchController_search","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiSearchDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AiSearchResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Search case evidence","tags":["ai"]}},"/ai/cases/{caseId}/progress":{"get":{"description":"Returns the AI processing status of the case for one evidence source (device messages by default): chunking, embedding and summarization progress with an overall percentage, timings, an embedding time estimate and the last error. Returns 404 when processing has not started for that source. Requires commenter access or above to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiProgressController_getProgress","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"source","required":false,"in":"query","description":"Source to inspect; defaults to device_message","schema":{"enum":["device_message","linkedin","reddit","discord","email","ai_chat"],"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/AiCaseProgressEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the AI processing progress","tags":["ai"]}},"/ai/cases/{caseId}/summary":{"get":{"description":"Returns the layered case summary: key parties, timeline, factual narrative (with any pending regenerated version), evidentiary map, attorney notes and open items. A case with no summary yet gets an empty one. Requires commenter access or above to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseSummaryController_getSummary","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The layered AI case summary.","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"version":{"type":"number","example":1},"keyParties":{"type":"object","properties":{"parties":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"frequency":{"type":"number"},"significance":{"type":"string"}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"timeline":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"description":{"type":"string"},"sourceRefs":{"type":"array","items":{"type":"string"}}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"factualNarrative":{"type":"object","properties":{"content":{"type":"string","nullable":true},"pendingVersion":{"type":"string","nullable":true},"pendingGeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"basedOnEvidenceThrough":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"stale":{"type":"boolean","example":false},"modelVersion":{"type":"string","nullable":true},"promptVersion":{"type":"string","nullable":true,"example":"case_summary.v1"},"contextProfileVersion":{"type":"number","nullable":true,"example":2}}},"evidentiaryMap":{"type":"object","properties":{"elements":{"type":"array","items":{"type":"object"}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"attorneyNotes":{"type":"object","properties":{"theory":{"type":"string","nullable":true},"strategy":{"type":"string","nullable":true},"openQuestions":{"type":"array","items":{"type":"string"}},"lastEditedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"lastEditedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"}}},"openItems":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["open","in_progress","resolved","dismissed"]},"suggestedBy":{"type":"string","enum":["ai_suggested","attorney_added"]},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"resolvedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the AI case summary","tags":["ai"]}},"/ai/cases/{caseId}/summary/regenerate":{"post":{"description":"Queues a rebuild of the factual narrative and answers 202 with `enqueued: true`. The new text lands in `factualNarrative.pendingVersion`, where it waits to be accepted or discarded. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseSummaryController_regenerate","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"enqueued":{"type":"boolean","example":true}}},"statusCode":{"type":"number","example":202},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Regenerate the case narrative","tags":["ai"]}},"/ai/cases/{caseId}/summary/accept-pending":{"post":{"description":"Replaces the factual narrative with its pending version, clears the pending fields and the stale flag, and returns the summary. Returns 400 when nothing is pending and 404 when the case has no summary. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseSummaryController_acceptPending","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The layered AI case summary.","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"version":{"type":"number","example":1},"keyParties":{"type":"object","properties":{"parties":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"frequency":{"type":"number"},"significance":{"type":"string"}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"timeline":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"description":{"type":"string"},"sourceRefs":{"type":"array","items":{"type":"string"}}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"factualNarrative":{"type":"object","properties":{"content":{"type":"string","nullable":true},"pendingVersion":{"type":"string","nullable":true},"pendingGeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"basedOnEvidenceThrough":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"stale":{"type":"boolean","example":false},"modelVersion":{"type":"string","nullable":true},"promptVersion":{"type":"string","nullable":true,"example":"case_summary.v1"},"contextProfileVersion":{"type":"number","nullable":true,"example":2}}},"evidentiaryMap":{"type":"object","properties":{"elements":{"type":"array","items":{"type":"object"}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"attorneyNotes":{"type":"object","properties":{"theory":{"type":"string","nullable":true},"strategy":{"type":"string","nullable":true},"openQuestions":{"type":"array","items":{"type":"string"}},"lastEditedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"lastEditedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"}}},"openItems":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["open","in_progress","resolved","dismissed"]},"suggestedBy":{"type":"string","enum":["ai_suggested","attorney_added"]},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"resolvedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Accept the pending narrative","tags":["ai"]}},"/ai/cases/{caseId}/summary/pending":{"delete":{"description":"Clears the pending narrative version and returns the summary. Returns 404 when the case has no summary. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseSummaryController_discardPending","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The layered AI case summary.","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"version":{"type":"number","example":1},"keyParties":{"type":"object","properties":{"parties":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"frequency":{"type":"number"},"significance":{"type":"string"}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"timeline":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"description":{"type":"string"},"sourceRefs":{"type":"array","items":{"type":"string"}}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"factualNarrative":{"type":"object","properties":{"content":{"type":"string","nullable":true},"pendingVersion":{"type":"string","nullable":true},"pendingGeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"basedOnEvidenceThrough":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"stale":{"type":"boolean","example":false},"modelVersion":{"type":"string","nullable":true},"promptVersion":{"type":"string","nullable":true,"example":"case_summary.v1"},"contextProfileVersion":{"type":"number","nullable":true,"example":2}}},"evidentiaryMap":{"type":"object","properties":{"elements":{"type":"array","items":{"type":"object"}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"attorneyNotes":{"type":"object","properties":{"theory":{"type":"string","nullable":true},"strategy":{"type":"string","nullable":true},"openQuestions":{"type":"array","items":{"type":"string"}},"lastEditedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"lastEditedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"}}},"openItems":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["open","in_progress","resolved","dismissed"]},"suggestedBy":{"type":"string","enum":["ai_suggested","attorney_added"]},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"resolvedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Discard the pending narrative","tags":["ai"]}},"/ai/cases/{caseId}/summary/attorney-notes":{"put":{"description":"Sets the attorney-written theory, strategy and open questions sent (the AI never changes these), records who edited them and when, and returns the summary, creating it if needed. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseSummaryController_updateAttorneyNotes","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiAttorneyNotesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The layered AI case summary.","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"version":{"type":"number","example":1},"keyParties":{"type":"object","properties":{"parties":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"frequency":{"type":"number"},"significance":{"type":"string"}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"timeline":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"description":{"type":"string"},"sourceRefs":{"type":"array","items":{"type":"string"}}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"factualNarrative":{"type":"object","properties":{"content":{"type":"string","nullable":true},"pendingVersion":{"type":"string","nullable":true},"pendingGeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"basedOnEvidenceThrough":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"stale":{"type":"boolean","example":false},"modelVersion":{"type":"string","nullable":true},"promptVersion":{"type":"string","nullable":true,"example":"case_summary.v1"},"contextProfileVersion":{"type":"number","nullable":true,"example":2}}},"evidentiaryMap":{"type":"object","properties":{"elements":{"type":"array","items":{"type":"object"}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"attorneyNotes":{"type":"object","properties":{"theory":{"type":"string","nullable":true},"strategy":{"type":"string","nullable":true},"openQuestions":{"type":"array","items":{"type":"string"}},"lastEditedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"lastEditedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"}}},"openItems":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["open","in_progress","resolved","dismissed"]},"suggestedBy":{"type":"string","enum":["ai_suggested","attorney_added"]},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"resolvedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update the attorney notes","tags":["ai"]}},"/ai/cases/{caseId}/context":{"get":{"description":"Returns the case context the AI uses as background: case type, jurisdiction, theory of the case, legal claims and their elements, what to prove, key parties and dates, with where each field came from. A case with no context yet gets an empty one. Requires commenter access or above to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseContextController_getContext","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The AI case context profile.","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"caseType":{"type":"string","nullable":true,"example":"custody"},"jurisdiction":{"type":"string","nullable":true,"example":"California"},"theoryOfCase":{"type":"string","nullable":true},"legalClaims":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"claim":{"type":"string"},"statuteCitation":{"type":"string","nullable":true},"elements":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"}}}},"source":{"type":"string","enum":["complaint","attorney_stated","amended_filing"]}}}},"whatToProve":{"type":"array","items":{"type":"string"}},"keyParties":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"role":{"type":"string","enum":["petitioner","respondent","plaintiff","defendant","child","witness","third_party","other"]},"handles":{"type":"array","items":{"type":"string"}},"significance":{"type":"string","nullable":true}}}},"keyDates":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"event":{"type":"string"},"significance":{"type":"string","nullable":true}}}},"relevantDateRange":{"type":"object","nullable":true,"properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}}},"sourceDocuments":{"type":"array","items":{"type":"string"}},"authoredBy":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e4"},"confirmedByAttorney":{"type":"boolean","example":false},"fieldProvenance":{"type":"object","description":"Per field: attorney_stated, ai_extracted or ai_extracted_confirmed.","additionalProperties":{"type":"string"},"example":{"theoryOfCase":"attorney_stated"}},"extractionModelVersion":{"type":"string","nullable":true},"version":{"type":"number","example":2},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the AI case context","tags":["ai"]},"patch":{"description":"Sets the fields sent and marks each as attorney-stated. `version` goes up when a field that steers the AI (case type, jurisdiction, theory, claims, what to prove, parties, dates or date range) actually changes. Returns the updated context. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseContextController_updateContext","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCaseContextPatchDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The AI case context profile.","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"caseType":{"type":"string","nullable":true,"example":"custody"},"jurisdiction":{"type":"string","nullable":true,"example":"California"},"theoryOfCase":{"type":"string","nullable":true},"legalClaims":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"claim":{"type":"string"},"statuteCitation":{"type":"string","nullable":true},"elements":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"}}}},"source":{"type":"string","enum":["complaint","attorney_stated","amended_filing"]}}}},"whatToProve":{"type":"array","items":{"type":"string"}},"keyParties":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"role":{"type":"string","enum":["petitioner","respondent","plaintiff","defendant","child","witness","third_party","other"]},"handles":{"type":"array","items":{"type":"string"}},"significance":{"type":"string","nullable":true}}}},"keyDates":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"event":{"type":"string"},"significance":{"type":"string","nullable":true}}}},"relevantDateRange":{"type":"object","nullable":true,"properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}}},"sourceDocuments":{"type":"array","items":{"type":"string"}},"authoredBy":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e4"},"confirmedByAttorney":{"type":"boolean","example":false},"fieldProvenance":{"type":"object","description":"Per field: attorney_stated, ai_extracted or ai_extracted_confirmed.","additionalProperties":{"type":"string"},"example":{"theoryOfCase":"attorney_stated"}},"extractionModelVersion":{"type":"string","nullable":true},"version":{"type":"number","example":2},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Update the AI case context","tags":["ai"]}},"/ai/cases/{caseId}/context/confirm":{"post":{"description":"Marks the context as reviewed by an attorney (`confirmedByAttorney: true`), after which the AI treats it as confirmed background. Returns 404 when the case has no context yet. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiCaseContextController_confirm","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The AI case context profile.","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"caseType":{"type":"string","nullable":true,"example":"custody"},"jurisdiction":{"type":"string","nullable":true,"example":"California"},"theoryOfCase":{"type":"string","nullable":true},"legalClaims":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"claim":{"type":"string"},"statuteCitation":{"type":"string","nullable":true},"elements":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"}}}},"source":{"type":"string","enum":["complaint","attorney_stated","amended_filing"]}}}},"whatToProve":{"type":"array","items":{"type":"string"}},"keyParties":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"role":{"type":"string","enum":["petitioner","respondent","plaintiff","defendant","child","witness","third_party","other"]},"handles":{"type":"array","items":{"type":"string"}},"significance":{"type":"string","nullable":true}}}},"keyDates":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"event":{"type":"string"},"significance":{"type":"string","nullable":true}}}},"relevantDateRange":{"type":"object","nullable":true,"properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}}},"sourceDocuments":{"type":"array","items":{"type":"string"}},"authoredBy":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e4"},"confirmedByAttorney":{"type":"boolean","example":false},"fieldProvenance":{"type":"object","description":"Per field: attorney_stated, ai_extracted or ai_extracted_confirmed.","additionalProperties":{"type":"string"},"example":{"theoryOfCase":"attorney_stated"}},"extractionModelVersion":{"type":"string","nullable":true},"version":{"type":"number","example":2},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Confirm the AI case context","tags":["ai"]}},"/ai/cases/{caseId}/context/extract-filing":{"post":{"description":"Queues extraction of case context from one of the case files (a complaint, petition or other filing) and answers 202 with the queued extraction; list the extractions to follow it. Returns 404 when the file is not an active file of the case. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiContextExtractionController_extractFiling","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartFilingExtractionDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"caseFile":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e5"},"status":{"type":"string","enum":["queued","running","completed","failed"],"example":"completed"},"rawExtraction":{"type":"object","nullable":true,"description":"The model output, kept as returned."},"appliedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"appliedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"appliedFields":{"type":"array","items":{"type":"string"},"example":["caseType","legalClaims"]},"modelVersion":{"type":"string","nullable":true},"promptVersion":{"type":"string","nullable":true,"example":"case_context_filing_extraction.v1"},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":202},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Extract case context from a filing","tags":["ai"]}},"/ai/cases/{caseId}/context/extractions":{"get":{"description":"Lists the latest 50 filing extractions of the case, newest first, with their status and, once completed, the raw extraction. Requires commenter access or above to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiContextExtractionController_listExtractions","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"caseFile":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e5"},"status":{"type":"string","enum":["queued","running","completed","failed"],"example":"completed"},"rawExtraction":{"type":"object","nullable":true,"description":"The model output, kept as returned."},"appliedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"appliedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"appliedFields":{"type":"array","items":{"type":"string"},"example":["caseType","legalClaims"]},"modelVersion":{"type":"string","nullable":true},"promptVersion":{"type":"string","nullable":true,"example":"case_context_filing_extraction.v1"},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List filing extractions","tags":["ai"]}},"/ai/cases/{caseId}/context/extractions/{extractionId}/apply":{"post":{"description":"Copies a completed extraction into the case context, all fields or only those in `fieldsToApply`, marking them as AI-extracted, and returns the extraction with `appliedAt`, `appliedBy` and `appliedFields`. Applying it again returns it unchanged. Returns 400 when the extraction is not completed and 404 when it does not exist or belongs to another case. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiContextExtractionController_applyExtraction","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"extractionId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyFilingExtractionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"caseFile":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e5"},"status":{"type":"string","enum":["queued","running","completed","failed"],"example":"completed"},"rawExtraction":{"type":"object","nullable":true,"description":"The model output, kept as returned."},"appliedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"appliedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"appliedFields":{"type":"array","items":{"type":"string"},"example":["caseType","legalClaims"]},"modelVersion":{"type":"string","nullable":true},"promptVersion":{"type":"string","nullable":true,"example":"case_context_filing_extraction.v1"},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Apply a filing extraction","tags":["ai"]}},"/ai/cases/{caseId}/evidentiary-map":{"get":{"description":"Returns, for each element of each legal claim, the supporting and contradicting evidence, its strength and the AI assessment, plus any attorney override. Returns 404 when the case has no summary yet. Requires commenter access or above to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiEvidentiaryMapController_getEvidentiaryMap","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The evidentiary map section of the case summary.","properties":{"elements":{"type":"array","items":{"type":"object","properties":{"claimId":{"type":"string","example":"claim-1"},"elementId":{"type":"string","example":"claim-1-el-1"},"elementDescription":{"type":"string"},"supportingEvidence":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["message","chunk","file"]},"ref":{"type":"string"},"source":{"type":"string","enum":["device_message","linkedin","reddit","discord","email","ai_chat"]},"snippet":{"type":"string","nullable":true},"relevanceScore":{"type":"number","example":0.82},"reasoning":{"type":"string","nullable":true}}}},"contradictingEvidence":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["message","chunk","file"]},"ref":{"type":"string"},"source":{"type":"string","enum":["device_message","linkedin","reddit","discord","email","ai_chat"]},"snippet":{"type":"string","nullable":true},"relevanceScore":{"type":"number","example":0.82},"reasoning":{"type":"string","nullable":true}}}},"strength":{"type":"string","enum":["gap","weak","moderate","strong"],"example":"moderate"},"aiAssessment":{"type":"string","nullable":true},"attorneyOverride":{"type":"string","nullable":true},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"stale":{"type":"boolean","example":false}}}},"lastRegeneratedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the evidentiary map","tags":["ai"]}},"/ai/cases/{caseId}/evidentiary-map/regenerate":{"post":{"description":"Queues a rebuild of every element and answers 202 with `enqueued: true`; read the map to see the result. Attorney overrides are kept. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiEvidentiaryMapController_regenerateMap","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"enqueued":{"type":"boolean","example":true}}},"statusCode":{"type":"number","example":202},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Regenerate the evidentiary map","tags":["ai"]}},"/ai/cases/{caseId}/evidentiary-map/regenerate-element":{"post":{"description":"Queues a rebuild of one claim element (`claimId`, `elementId`) and answers 202 with `enqueued: true`. Its attorney override is kept. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiEvidentiaryMapController_regenerateElement","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RebuildEvidentiaryElementDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"enqueued":{"type":"boolean","example":true}}},"statusCode":{"type":"number","example":202},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Regenerate one evidentiary map element","tags":["ai"]}},"/ai/cases/{caseId}/evidentiary-map/elements/{claimId}/{elementId}/override":{"put":{"description":"Sets the attorney override text of one claim element, or clears it when `override` is null or omitted; regeneration never changes it. Returns 404 when the claim or element does not exist. Requires owner or admin access to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiEvidentiaryMapController_setOverride","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"claimId","required":true,"in":"path","schema":{"type":"string"}},{"name":"elementId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttorneyOverrideDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"ok":{"type":"boolean","example":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Set an attorney override on an element","tags":["ai"]}},"/ai/cases/{caseId}/chunks/{chunkId}/suggest-tags":{"post":{"description":"Returns active system, organization and case tags ranked by confidence with the reasoning, plus names for tags that do not exist yet. Nothing is applied. Results are cached; `forceRefresh` asks the model again. Returns 404 when the chunk is not in the case or its source is not supported. Requires commenter access or above to the case and an active plan (403 otherwise); not available to opposing counsel.","operationId":"AiTagSuggestionController_suggestTags","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}},{"name":"chunkId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"suggestions":{"type":"array","items":{"type":"object","properties":{"tagId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e6"},"tagName":{"type":"string","example":"Threats"},"tagSeverity":{"type":"string"},"tagBackgroundColor":{"type":"string","example":"#FDE2E1"},"tagTextColor":{"type":"string","example":"#9B1C1C"},"confidence":{"type":"number","example":0.87},"reasoning":{"type":"string"}}}},"noveltyHints":{"type":"array","items":{"type":"object","properties":{"proposedTagName":{"type":"string","example":"Missed pickups"},"reasoning":{"type":"string"}}}},"cached":{"type":"boolean","example":false},"model":{"type":"string","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Suggest tags for a conversation chunk","tags":["ai"]}},"/ai/admin/prompts":{"get":{"description":"Lists the active stored prompt version of each purpose, sorted by purpose. Only stored versions are listed; a purpose with none runs on its code default. Super admins only.","operationId":"AiPromptAdminController_listActive","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"purpose":{"type":"string","enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"example":"case_summary"},"versionSlug":{"type":"string","example":"case_summary.v2"},"systemPrompt":{"type":"string"},"toolName":{"type":"string"},"toolDescription":{"type":"string"},"toolInputSchema":{"type":"object","description":"JSON Schema of the tool input."},"model":{"type":"string","example":"sonnet"},"maxTokens":{"type":"number","example":1024},"temperature":{"type":"number","example":0.2},"isActive":{"type":"boolean","example":true},"notes":{"type":"string"},"createdBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"updatedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List the active AI prompts (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/prompts/{purpose}":{"get":{"description":"Lists every stored version of one prompt purpose, newest first. An unknown `purpose` returns 400. Super admins only.","operationId":"AiPromptAdminController_listVersions","parameters":[{"name":"purpose","required":true,"in":"path","schema":{"enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"purpose":{"type":"string","enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"example":"case_summary"},"versionSlug":{"type":"string","example":"case_summary.v2"},"systemPrompt":{"type":"string"},"toolName":{"type":"string"},"toolDescription":{"type":"string"},"toolInputSchema":{"type":"object","description":"JSON Schema of the tool input."},"model":{"type":"string","example":"sonnet"},"maxTokens":{"type":"number","example":1024},"temperature":{"type":"number","example":0.2},"isActive":{"type":"boolean","example":true},"notes":{"type":"string"},"createdBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"updatedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List the versions of an AI prompt (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/prompts/{purpose}/active":{"get":{"description":"Returns the prompt the pipeline uses for one purpose: the active stored version, or the code default when none is stored. The result is cached for up to a minute and has a different shape from a stored version (the tool fields are grouped under `tool`). An unknown `purpose` returns 400. Super admins only.","operationId":"AiPromptAdminController_getActive","parameters":[{"name":"purpose","required":true,"in":"path","schema":{"enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"purpose":{"type":"string","enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"example":"case_summary"},"versionSlug":{"type":"string","example":"case_summary.v2"},"systemPrompt":{"type":"string"},"tool":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"input_schema":{"type":"object"}}},"model":{"type":"string","example":"sonnet"},"maxTokens":{"type":"number","example":1024},"temperature":{"type":"number","example":0.2}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the active AI prompt (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/prompts/{purpose}/versions/{versionSlug}":{"get":{"description":"Returns one stored version of a prompt purpose. Returns 404 when the version does not exist. An unknown `purpose` returns 400. Super admins only.","operationId":"AiPromptAdminController_getVersion","parameters":[{"name":"purpose","required":true,"in":"path","schema":{"enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"type":"string"}},{"name":"versionSlug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"purpose":{"type":"string","enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"example":"case_summary"},"versionSlug":{"type":"string","example":"case_summary.v2"},"systemPrompt":{"type":"string"},"toolName":{"type":"string"},"toolDescription":{"type":"string"},"toolInputSchema":{"type":"object","description":"JSON Schema of the tool input."},"model":{"type":"string","example":"sonnet"},"maxTokens":{"type":"number","example":1024},"temperature":{"type":"number","example":0.2},"isActive":{"type":"boolean","example":true},"notes":{"type":"string"},"createdBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"updatedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get one AI prompt version (internal)","tags":["ai-admin"],"x-internal":true},"patch":{"description":"Edits the system prompt, tool description or input schema, model, token limit, temperature or notes of a version without changing its `versionSlug`, so meant for small fixes; use a new draft for material changes. Renaming or removing top-level tool input properties returns 400; an unknown version returns 404. An unknown `purpose` returns 400. Super admins only.","operationId":"AiPromptAdminController_updateVersion","parameters":[{"name":"purpose","required":true,"in":"path","schema":{"enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"type":"string"}},{"name":"versionSlug","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPromptUpdateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"purpose":{"type":"string","enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"example":"case_summary"},"versionSlug":{"type":"string","example":"case_summary.v2"},"systemPrompt":{"type":"string"},"toolName":{"type":"string"},"toolDescription":{"type":"string"},"toolInputSchema":{"type":"object","description":"JSON Schema of the tool input."},"model":{"type":"string","example":"sonnet"},"maxTokens":{"type":"number","example":1024},"temperature":{"type":"number","example":0.2},"isActive":{"type":"boolean","example":true},"notes":{"type":"string"},"createdBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"updatedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Edit an AI prompt version in place (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/prompts/{purpose}/drafts":{"post":{"description":"Creates an inactive version with the next `versionSlug` for the purpose, copied from `seedFromVersionSlug` (or the active version, or the code default) with the given changes applied. It takes no traffic until activated. Renaming or removing top-level tool input properties returns 400. An unknown `purpose` returns 400. Super admins only.","operationId":"AiPromptAdminController_createDraft","parameters":[{"name":"purpose","required":true,"in":"path","schema":{"enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPromptCreateDraftDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"purpose":{"type":"string","enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"example":"case_summary"},"versionSlug":{"type":"string","example":"case_summary.v2"},"systemPrompt":{"type":"string"},"toolName":{"type":"string"},"toolDescription":{"type":"string"},"toolInputSchema":{"type":"object","description":"JSON Schema of the tool input."},"model":{"type":"string","example":"sonnet"},"maxTokens":{"type":"number","example":1024},"temperature":{"type":"number","example":0.2},"isActive":{"type":"boolean","example":true},"notes":{"type":"string"},"createdBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"updatedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create an AI prompt draft (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/prompts/{purpose}/versions/{versionSlug}/activate":{"post":{"description":"Makes one version the active prompt for its purpose and deactivates the previous one; activating the active version changes nothing. Returns 404 when the version does not exist. An unknown `purpose` returns 400. Super admins only.","operationId":"AiPromptAdminController_activate","parameters":[{"name":"purpose","required":true,"in":"path","schema":{"enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"type":"string"}},{"name":"versionSlug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"purpose":{"type":"string","enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"example":"case_summary"},"versionSlug":{"type":"string","example":"case_summary.v2"},"systemPrompt":{"type":"string"},"toolName":{"type":"string"},"toolDescription":{"type":"string"},"toolInputSchema":{"type":"object","description":"JSON Schema of the tool input."},"model":{"type":"string","example":"sonnet"},"maxTokens":{"type":"number","example":1024},"temperature":{"type":"number","example":0.2},"isActive":{"type":"boolean","example":true},"notes":{"type":"string"},"createdBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"updatedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Activate an AI prompt version (internal)","tags":["ai-admin"],"x-internal":true}},"/ai/admin/prompts/{purpose}/reset-to-code-default":{"post":{"description":"Restores the code-default version of the purpose (creating it when missing) to the default text and settings, activates it and returns it. An unknown `purpose` returns 400. Super admins only.","operationId":"AiPromptAdminController_resetToCodeDefault","parameters":[{"name":"purpose","required":true,"in":"path","schema":{"enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"purpose":{"type":"string","enum":["chunk_summary","period_summary","conversation_summary","case_summary","case_context_filing_extraction","relevance_gate","evidentiary_map_element","tag_suggestion"],"example":"case_summary"},"versionSlug":{"type":"string","example":"case_summary.v2"},"systemPrompt":{"type":"string"},"toolName":{"type":"string"},"toolDescription":{"type":"string"},"toolInputSchema":{"type":"object","description":"JSON Schema of the tool input."},"model":{"type":"string","example":"sonnet"},"maxTokens":{"type":"number","example":1024},"temperature":{"type":"number","example":0.2},"isActive":{"type":"boolean","example":true},"notes":{"type":"string"},"createdBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"updatedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true},"updatedAt":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z","nullable":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Reset an AI prompt to its code default (internal)","tags":["ai-admin"],"x-internal":true}},"/financial-data/sandbox/public_token/create":{"post":{"description":"Developer tool: asks the Plaid sandbox for a public token (what Plaid Link hands back after someone connects a bank) for a test institution and set of products. Returns 400 unless the server is pointed at the Plaid sandbox.","operationId":"FinancialDataController_createSandboxPublicToken","parameters":[],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxPublicTokenDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/SandboxPublicTokenResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Create a Plaid sandbox public token (internal)","tags":["financial-data"],"x-internal":true}},"/financial-data/investment-transactions/get":{"get":{"description":"Returns one page of investment transactions (buys, sells, dividends and so on) for a financial account, with ticker, security name and attached tags; filter by date range, type or tags. On a case shared with opposing counsel, an account the caller may not see returns an empty page, and attachedTags leave out tags the other side added by hand. Returns 404 when a Plaid item or account id sent belongs to another case.","operationId":"FinancialDataController_getInvestmentTransactions","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"caseId","required":true,"in":"query","description":"Case ID this financial data belongs to.","schema":{"type":"string"}},{"name":"financialAccountId","required":true,"in":"query","description":"Internal FinancialAccount document ID.","schema":{"type":"string"}},{"name":"startDate","required":false,"in":"query","description":"Start date filter (YYYY-MM-DD, inclusive).","schema":{"example":"2024-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"End date filter (YYYY-MM-DD, inclusive).","schema":{"example":"2024-12-31","type":"string"}},{"name":"transactionType","required":false,"in":"query","description":"Filter by transaction type (e.g. buy, sell, dividend, transfer).","schema":{"example":"sell","type":"string"}},{"name":"tagIds","required":false,"in":"query","description":"Filter by tag IDs (reserved for future use).","schema":{"type":"array","items":{"type":"string"}}},{"name":"sortBy","required":false,"in":"query","description":"Field to sort by.","schema":{"default":"date","type":"string","enum":["date"]}},{"name":"sortOrder","required":false,"in":"query","description":"Sort direction.","schema":{"default":"desc","type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InvestmentTransactionDataEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List investment transactions","tags":["financial-data"]}},"/financial-data/plaid-item/{plaidItemId}/status":{"get":{"description":"Calls Plaid's /item/get for the connected bank login and returns Plaid's response unchanged. Case owners and admins only; returns 409 if the organization has not finished Plaid onboarding.","operationId":"FinancialDataController_getPlaidItemStatus","parameters":[{"name":"plaidItemId","required":true,"in":"path","description":"PlaidItem MongoDB ID","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"Plaid's /item/get response, passed through unchanged. Its shape is defined by Plaid, not by this API."},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a Plaid item's live status","tags":["financial-data"]}},"/financial-data/plaid-item/{plaidItemId}/remove":{"delete":{"description":"Revokes the bank connection on Plaid (/item/remove, with an optional reason), marks the item inactive and records a case event. Case owners and admins only, not available to opposing counsel; returns 400 if the item is already inactive.","operationId":"FinancialDataController_removePlaidItem","parameters":[{"name":"plaidItemId","required":true,"in":"path","description":"PlaidItem MongoDB ID","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"query","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemovePlaidItemDto"}}}},"responses":{"204":{"description":"Item disconnected. No response body."}},"security":[{"access-token":[]}],"summary":"Disconnect a Plaid item","tags":["financial-data"]}},"/financial-data/accounts/data":{"get":{"description":"Returns one page of financial accounts with balances and the provider they came from, for one Plaid item or, when none is given, every item linked to the case; filter by account type. On a case shared with opposing counsel, accounts the caller may not see are excluded; ocSharedStatus (the account's sharing status) is filled in for opposing counsel, who can also filter by it, and is null for everyone else. Returns 404 when a Plaid item or account id sent belongs to another case.","operationId":"FinancialDataController_getAccountsData","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"caseId","required":true,"in":"query","description":"Case ID this financial data belongs to.","schema":{"type":"string"}},{"name":"plaidItemId","required":false,"in":"query","description":"Internal PlaidItem document ID returned by the public token exchange endpoint. Optional for extraction code users — resolved automatically from caseId.","schema":{"type":"string"}},{"name":"accountType","required":false,"in":"query","description":"Filter accounts by type.","schema":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}},{"name":"financialAccountId","required":false,"in":"query","description":"Filter by a specific financial account ID.","schema":{"type":"string"}},{"name":"sharedStatus","required":false,"in":"query","description":"Opposing-council only. Narrows to accounts in one sharing state; ignored for OL/OG and extraction-code callers.","schema":{"type":"string","enum":["shared","pending","not_gated"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AccountDataEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List financial accounts","tags":["financial-data"]}},"/financial-data/investments/holdings/get":{"get":{"description":"Returns one page of investment holdings (security, quantity, price, value, cost basis) for an account, a Plaid item, or every item linked to the case, sorted by value; filter by security type or tags. On a case shared with opposing counsel, accounts the caller may not see are excluded, and attachedTags leave out tags the other side added by hand. Returns 404 when a Plaid item or account id sent belongs to another case.","operationId":"FinancialDataController_getInvestmentHoldings","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"caseId","required":true,"in":"query","description":"Case ID this financial data belongs to.","schema":{"type":"string"}},{"name":"plaidItemId","required":false,"in":"query","description":"Internal PlaidItem document ID. Optional for extraction code users — resolved automatically from caseId.","schema":{"type":"string"}},{"name":"type","required":false,"in":"query","description":"Filter by security type (e.g. equity, etf, derivative).","schema":{"type":"string"}},{"name":"financialAccountId","required":false,"in":"query","description":"Filter by a specific financial account ID.","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","description":"Field to sort by.","schema":{"default":"totalValue","type":"string","enum":["totalValue"]}},{"name":"sortOrder","required":false,"in":"query","description":"Sort direction.","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"tagIds","required":false,"in":"query","description":"Filter holdings that have at least one of the provided tag IDs.","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HoldingDataEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List investment holdings","tags":["financial-data"]}},"/financial-data/liabilities/get":{"get":{"description":"Returns one page of liabilities for an account, a Plaid item, or every item linked to the case, split into credit card, mortgage and student loan lists; filter by type, overdue or tags. On a case shared with opposing counsel, accounts the caller may not see are excluded, and attachedTags leave out tags the other side added by hand. Returns 404 when a Plaid item or account id sent belongs to another case.","operationId":"FinancialDataController_getLiabilities","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"caseId","required":true,"in":"query","description":"Case ID this financial data belongs to.","schema":{"type":"string"}},{"name":"plaidItemId","required":false,"in":"query","description":"Internal PlaidItem document ID. Optional for extraction code users — resolved automatically from caseId.","schema":{"type":"string"}},{"name":"liabilityType","required":false,"in":"query","description":"Filter by liability type.","schema":{"type":"string","enum":["credit","mortgage","student"]}},{"name":"isOverdue","required":false,"in":"query","description":"Filter by overdue status. Applies to credit and student liabilities only.","schema":{"type":"boolean"}},{"name":"financialAccountId","required":false,"in":"query","description":"Filter by a specific financial account ID.","schema":{"type":"string"}},{"name":"tagIds","required":false,"in":"query","description":"Filter liabilities that have at least one of the provided tag IDs.","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/GetLiabilitiesResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List liabilities","tags":["financial-data"]}},"/financial-data/plaid-item/{plaidItemId}/resync":{"post":{"description":"Support tool: resets the chosen Plaid products (all of transactions, recurring transactions, investments and liabilities when none are given) and queues fresh sync jobs for the item. Case owners and admins only, not available to opposing counsel; returns 400 if the item is inactive.","operationId":"FinancialSyncRetryController_resyncPlaidItem","parameters":[{"name":"plaidItemId","required":true,"in":"path","description":"PlaidItem MongoDB ID","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"query","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetrySyncDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["statusCode","timestamp"],"properties":{"statusCode":{"type":"number","example":202},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Re-run Plaid syncs for an item (internal)","tags":["financial-data"],"x-internal":true}},"/financial-data/transactions/get":{"get":{"description":"Returns one page of transactions for a financial account, with amounts as positive numbers plus a debit/credit type, the effective category (your override if set) and attached tags. Filter by category, subcategory, name, merchant, date range, amount range, debit/credit or tags. On a case shared with opposing counsel, an account the caller may not see returns an empty page, and attachedTags leave out tags the other side added by hand. Returns 404 when a Plaid item or account id sent belongs to another case.","operationId":"TransactionsController_getTransactions","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"caseId","required":true,"in":"query","description":"Case ID this financial data belongs to.","schema":{"type":"string"}},{"name":"plaidItemId","required":false,"in":"query","description":"Internal PlaidItem document ID. Optional for extraction code users — resolved automatically from caseId.","schema":{"type":"string"}},{"name":"financialAccountId","required":true,"in":"query","description":"Internal FinancialAccount document ID.","schema":{"type":"string"}},{"name":"category","required":false,"in":"query","description":"Filter by transaction category (categoryPrimary). Case-insensitive partial match.","schema":{"type":"string"}},{"name":"subcategory","required":false,"in":"query","description":"Filter by transaction subcategory (Plaid categoryDetailed). Case-insensitive partial match. Only matches transactions whose primary category has not been overridden.","schema":{"type":"string"}},{"name":"name","required":false,"in":"query","description":"Filter by transaction name. Case-insensitive partial match.","schema":{"type":"string"}},{"name":"merchant","required":false,"in":"query","description":"Filter to a single merchant. Exact match on merchantName, as returned by the transaction list. Transactions with no merchant name never match.","schema":{"type":"string"}},{"name":"startDate","required":false,"in":"query","description":"Filter transactions from this date (inclusive). Format: YYYY-MM-DD.","schema":{"type":"string"}},{"name":"endDate","required":false,"in":"query","description":"Filter transactions up to this date (inclusive). Format: YYYY-MM-DD.","schema":{"type":"string"}},{"name":"minAmount","required":false,"in":"query","description":"Filter transactions with amount >= minAmount.","schema":{"type":"number"}},{"name":"maxAmount","required":false,"in":"query","description":"Filter transactions with amount <= maxAmount (absolute value).","schema":{"type":"number"}},{"name":"transactionType","required":false,"in":"query","description":"Filter by transaction type. debit = money out (positive in Plaid), credit = money in (negative in Plaid).","schema":{"type":"string","enum":["debit","credit"]}},{"name":"sortBy","required":false,"in":"query","description":"Field to sort by.","schema":{"default":"date","type":"string","enum":["date","amount","category"]}},{"name":"sortOrder","required":false,"in":"query","description":"Sort direction.","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"tagIds","required":false,"in":"query","description":"Filter transactions that have all of the provided tag IDs.","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TransactionDataEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List transactions","tags":["financial-data"]}},"/financial-data/account/{accountId}/transactions/summary":{"get":{"description":"Returns debit and credit totals and a transaction count per category for one account, plus overall totals; optionally filter by category text and date range, or nest subcategories with groupBy=detailed. On a case shared with opposing counsel, an account the caller may not see returns an empty summary. Returns 404 when a Plaid item or account id sent belongs to another case.","operationId":"TransactionsController_getTransactionsSummary","parameters":[{"name":"accountId","required":true,"in":"path","description":"Financial account ID","schema":{"type":"string"}},{"name":"caseId","required":true,"in":"query","description":"Case ID this financial data belongs to.","schema":{"type":"string"}},{"name":"plaidItemId","required":false,"in":"query","description":"Internal PlaidItem document ID. Optional — resolved automatically from accountId.","schema":{"type":"string"}},{"name":"category","required":false,"in":"query","description":"Filter by category (case-insensitive partial match on categoryPrimary). If omitted, all categories are included.","schema":{"type":"string"}},{"name":"startDate","required":false,"in":"query","description":"Filter transactions from this date (inclusive). Format: YYYY-MM-DD.","schema":{"example":"2024-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"Filter transactions up to this date (inclusive). Format: YYYY-MM-DD.","schema":{"example":"2024-12-31","type":"string"}},{"name":"groupBy","required":false,"in":"query","description":"Category granularity. `detailed` nests Plaid subcategories under each category; `primary` (default) returns the category breakdown only.","schema":{"default":"primary","type":"string","enum":["primary","detailed"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/GetTransactionsSummaryResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Summarize an account's transactions by category","tags":["financial-data"]}},"/financial-data/case/{caseId}/transactions/monthly-category-summary":{"get":{"description":"Returns debit and credit totals and counts per category for each month, across all the case's accounts or the ones given, with optional category and date filters. On a case shared with opposing counsel, only accounts the caller may see are counted. Returns 403 while the monthly category summary feature is turned off.","operationId":"TransactionsController_getMonthlyCategorySummary","parameters":[{"name":"caseId","required":true,"in":"path","description":"Case ID","schema":{"type":"string"}},{"name":"accountIds","required":false,"in":"query","description":"Restrict the aggregation to these FinancialAccount IDs (all must belong to the case). Omit to include every linked account in the case. Accepts a repeated query param or a comma-separated list.","schema":{"type":"array","items":{"type":"string"}}},{"name":"category","required":false,"in":"query","description":"Filter by one or more categories (case-insensitive partial match on categoryPrimary; matches any of them). Accepts a repeated query param or a comma-separated list. If omitted, every category is returned per month.","schema":{"type":"array","items":{"type":"string"}}},{"name":"startDate","required":false,"in":"query","description":"Include transactions from this date (inclusive). Format: YYYY-MM-DD.","schema":{"example":"2024-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"Include transactions up to this date (inclusive). Format: YYYY-MM-DD.","schema":{"example":"2024-12-31","type":"string"}},{"name":"groupBy","required":false,"in":"query","description":"Category granularity. `detailed` nests Plaid subcategories under each broad category; `primary` (default) returns the broad-category breakdown only.","schema":{"default":"primary","type":"string","enum":["primary","detailed"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/GetMonthlyCategorySummaryResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Summarize a case's transactions by month and category","tags":["financial-data"]}},"/financial-data/case/{caseId}/transactions/monthly-category-summary/export":{"get":{"description":"Same data as the monthly category summary, returned directly as a CSV file attachment instead of JSON. On a case shared with opposing counsel, only accounts the caller may see are counted. Returns 403 while the monthly category summary feature is turned off.","operationId":"TransactionsController_exportMonthlyCategorySummary","parameters":[{"name":"caseId","required":true,"in":"path","description":"Case ID","schema":{"type":"string"}},{"name":"accountIds","required":false,"in":"query","description":"Restrict the aggregation to these FinancialAccount IDs (all must belong to the case). Omit to include every linked account in the case. Accepts a repeated query param or a comma-separated list.","schema":{"type":"array","items":{"type":"string"}}},{"name":"category","required":false,"in":"query","description":"Filter by one or more categories (case-insensitive partial match on categoryPrimary; matches any of them). Accepts a repeated query param or a comma-separated list. If omitted, every category is returned per month.","schema":{"type":"array","items":{"type":"string"}}},{"name":"startDate","required":false,"in":"query","description":"Include transactions from this date (inclusive). Format: YYYY-MM-DD.","schema":{"example":"2024-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"Include transactions up to this date (inclusive). Format: YYYY-MM-DD.","schema":{"example":"2024-12-31","type":"string"}},{"name":"groupBy","required":false,"in":"query","description":"Category granularity. `detailed` nests Plaid subcategories under each broad category; `primary` (default) returns the broad-category breakdown only.","schema":{"default":"primary","type":"string","enum":["primary","detailed"]}}],"responses":{"200":{"description":"CSV file (text/csv attachment). Not wrapped in the JSON envelope.","content":{"text/csv":{"schema":{"type":"string"}}}}},"security":[{"access-token":[]}],"summary":"Download the monthly category summary as CSV","tags":["financial-data"]}},"/financial-data/case/{caseId}/transactions/categories":{"get":{"description":"Returns each category, with its subcategories, that has transactions across the case's accounts or the ones given, with a count at both levels and the account ids used; optional date range. On a case shared with opposing counsel, only accounts the caller may see are counted. Returns 403 while the monthly category summary feature is turned off.","operationId":"TransactionsController_getCaseCategories","parameters":[{"name":"caseId","required":true,"in":"path","description":"Case ID","schema":{"type":"string"}},{"name":"accountIds","required":false,"in":"query","description":"Restrict the lookup to these FinancialAccount IDs (all must belong to the case). Omit to include every linked account in the case. Accepts a repeated query param or a comma-separated list.","schema":{"type":"array","items":{"type":"string"}}},{"name":"startDate","required":false,"in":"query","description":"Only consider transactions from this date (inclusive). Format: YYYY-MM-DD.","schema":{"example":"2024-01-01","type":"string"}},{"name":"endDate","required":false,"in":"query","description":"Only consider transactions up to this date (inclusive). Format: YYYY-MM-DD.","schema":{"example":"2024-12-31","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/GetCaseCategoriesResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a case's transaction categories","tags":["financial-data"]}},"/financial-data/case/{caseId}/transactions/{transactionId}/category":{"patch":{"description":"Sets your own category on a transaction in the case (Plaid's original is kept); omit the category or send null to go back to Plaid's. Can also apply the change to the case's other transactions from the same merchant. Returns the effective category and how many transactions changed. Not available to opposing counsel.","operationId":"TransactionsController_updateTransactionCategory","parameters":[{"name":"caseId","required":true,"in":"path","description":"Case ID","schema":{"type":"string"}},{"name":"transactionId","required":true,"in":"path","description":"Transaction ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTransactionCategoryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/UpdateTransactionCategoryResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Change a transaction's category","tags":["financial-data"]}},"/financial-data/recurring-transactions/get":{"get":{"description":"Returns one page of recurring transaction streams (regular payments or deposits Plaid detected) for a financial account, with amounts, frequency, dates and attached tags; filter by stream type, active flag, frequency or tags. On a case shared with opposing counsel, an account the caller may not see returns an empty page, and attachedTags leave out tags the other side added by hand.","operationId":"TransactionsController_getRecurringTransactionsStream","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"caseId","required":true,"in":"query","description":"Case ID this financial data belongs to.","schema":{"type":"string"}},{"name":"plaidItemId","required":false,"in":"query","description":"Internal PlaidItem document ID. Optional for extraction code users — resolved automatically from caseId.","schema":{"type":"string"}},{"name":"financialAccountId","required":true,"in":"query","description":"Internal FinancialAccount document ID.","schema":{"type":"string"}},{"name":"type","required":false,"in":"query","description":"Filter by stream type.","schema":{"type":"string","enum":["inflow","outflow"]}},{"name":"isActive","required":false,"in":"query","description":"Filter by active status.","schema":{"type":"boolean"}},{"name":"frequency","required":false,"in":"query","description":"Filter by frequency (e.g. WEEKLY, MONTHLY).","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","description":"Field to sort by.","schema":{"default":"lastDate","type":"string","enum":["averageAmount","lastDate","firstDate"]}},{"name":"sortOrder","required":false,"in":"query","description":"Sort direction.","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"tagIds","required":false,"in":"query","description":"Filter recurring transactions that have at least one of the provided tag IDs.","schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RecurringTransactionStreamDataEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List recurring transactions","tags":["financial-data"]}},"/financial-data/transaction/{transactionId}/tags":{"post":{"description":"Adds tags to a transaction by name or id; names that do not exist yet are created as case tags. The transaction must belong to the case. Returns the full transaction row after the change; on a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"TransactionTagsController_addTags","parameters":[{"name":"transactionId","required":true,"in":"path","description":"Transaction ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTransactionTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/TransactionDataEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag a transaction","tags":["financial-data"]},"delete":{"description":"Removes the given tags (by name or id) from a transaction in the case and returns the full transaction row after the change. Returns 400 if any of them is not on the transaction. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"TransactionTagsController_removeTags","parameters":[{"name":"transactionId","required":true,"in":"path","description":"Transaction ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveTransactionTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/TransactionDataEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove tags from a transaction","tags":["financial-data"]}},"/financial-data/transactions/tags/bulk":{"post":{"description":"Adds the same tags to every listed transaction and returns the updated rows in a data array. If any transaction fails, the request returns 400 with the errors, but tags already added to the others stay. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"TransactionTagsController_bulkAddTags","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkAddTransactionTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TransactionDataEntity"}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag several transactions","tags":["financial-data"]}},"/financial-data/recurring-transaction/{streamId}/tags":{"post":{"description":"Adds tags to a recurring transaction by name or id; names that do not exist yet are created as case tags. The recurring transaction must belong to the case. Returns the full recurring transaction row after the change; on a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"RecurringTransactionTagsController_addTags","parameters":[{"name":"streamId","required":true,"in":"path","description":"Recurring transaction stream ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTransactionTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RecurringTransactionStreamDataEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag a recurring transaction","tags":["financial-data"]},"delete":{"description":"Removes the given tags (by name or id) from a recurring transaction in the case and returns the full recurring transaction row after the change. Returns 400 if any of them is not on the recurring transaction. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"RecurringTransactionTagsController_removeTags","parameters":[{"name":"streamId","required":true,"in":"path","description":"Recurring transaction stream ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveTransactionTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/RecurringTransactionStreamDataEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove tags from a recurring transaction","tags":["financial-data"]}},"/financial-data/recurring-transactions/tags/bulk":{"post":{"description":"Adds the same tags to every listed recurring transaction and returns the updated rows in a data array. If any recurring transaction fails, the request returns 400 with the errors, but tags already added to the others stay. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"RecurringTransactionTagsController_bulkAddTags","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkAddRecurringTransactionTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RecurringTransactionStreamDataEntity"}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag several recurring transactions","tags":["financial-data"]}},"/financial-data/liability/{liabilityId}/tags":{"post":{"description":"Adds tags to a liability by name or id; names that do not exist yet are created as case tags. The liability must belong to the case. Returns the liability's id, type, dates and tags after the change; on a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"LiabilityTagsController_addTags","parameters":[{"name":"liabilityId","required":true,"in":"path","description":"Liability ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTransactionTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"financialAccount":{"type":"string"},"plaidAccountId":{"type":"string"},"liabilityType":{"type":"string","example":"credit"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag a liability","tags":["financial-data"]},"delete":{"description":"Removes the given tags (by name or id) from a liability in the case and returns the liability's id, type, dates and tags after the change. Returns 400 if any of them is not on the liability. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"LiabilityTagsController_removeTags","parameters":[{"name":"liabilityId","required":true,"in":"path","description":"Liability ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveTransactionTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"financialAccount":{"type":"string"},"plaidAccountId":{"type":"string"},"liabilityType":{"type":"string","example":"credit"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove tags from a liability","tags":["financial-data"]}},"/financial-data/liabilities/tags/bulk":{"post":{"description":"Adds the same tags to every listed liability and returns the updated rows in a data array. If any liability fails, the request returns 400 with the errors, but tags already added to the others stay. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"LiabilityTagsController_bulkAddTags","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkAddLiabilityTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"financialAccount":{"type":"string"},"plaidAccountId":{"type":"string"},"liabilityType":{"type":"string","example":"credit"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}}}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag several liabilities","tags":["financial-data"]}},"/financial-data/holding/{holdingId}/tags":{"post":{"description":"Adds tags to a holding by name or id; names that do not exist yet are created as case tags. The holding must belong to the case. Returns the holding's id, dates and tags after the change; on a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"HoldingTagsController_addTags","parameters":[{"name":"holdingId","required":true,"in":"path","description":"Holding ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTransactionTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"financialAccount":{"type":"string"},"plaidSecurityId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag a holding","tags":["financial-data"]},"delete":{"description":"Removes the given tags (by name or id) from a holding in the case and returns the holding's id, dates and tags after the change. Returns 400 if any of them is not on the holding. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"HoldingTagsController_removeTags","parameters":[{"name":"holdingId","required":true,"in":"path","description":"Holding ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveTransactionTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"financialAccount":{"type":"string"},"plaidSecurityId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove tags from a holding","tags":["financial-data"]}},"/financial-data/holdings/tags/bulk":{"post":{"description":"Adds the same tags to every listed holding and returns the updated rows in a data array. If any holding fails, the request returns 400 with the errors, but tags already added to the others stay. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"HoldingTagsController_bulkAddTags","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkAddHoldingTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"financialAccount":{"type":"string"},"plaidSecurityId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}}}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag several holdings","tags":["financial-data"]}},"/financial-data/investment-transaction/{investmentTransactionId}/tags":{"post":{"description":"Adds tags to an investment transaction by name or id; names that do not exist yet are created as case tags. The investment transaction must belong to the case. Returns the transaction's core fields (date, type, name, amount) and tags after the change; on a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"InvestmentTransactionTagsController_addTags","parameters":[{"name":"investmentTransactionId","required":true,"in":"path","description":"Investment transaction ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTransactionTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"financialAccountId":{"type":"string"},"date":{"type":"string","format":"date-time"},"type":{"type":"string"},"subtype":{"type":"string"},"name":{"type":"string"},"amount":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag an investment transaction","tags":["financial-data"]},"delete":{"description":"Removes the given tags (by name or id) from an investment transaction in the case and returns the transaction's core fields (date, type, name, amount) and tags after the change. Returns 400 if any of them is not on the investment transaction. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"InvestmentTransactionTagsController_removeTags","parameters":[{"name":"investmentTransactionId","required":true,"in":"path","description":"Investment transaction ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveTransactionTagsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"financialAccountId":{"type":"string"},"date":{"type":"string","format":"date-time"},"type":{"type":"string"},"subtype":{"type":"string"},"name":{"type":"string"},"amount":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove tags from an investment transaction","tags":["financial-data"]}},"/financial-data/investment-transactions/tags/bulk":{"post":{"description":"Adds the same tags to every listed investment transaction and returns the updated rows in a data array. If any investment transaction fails, the request returns 400 with the errors, but tags already added to the others stay. On a case shared with opposing counsel, attachedTags leave out tags the other side added by hand.","operationId":"InvestmentTransactionTagsController_bulkAddTags","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkAddInvestmentTransactionTagsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"financialAccountId":{"type":"string"},"date":{"type":"string","format":"date-time"},"type":{"type":"string"},"subtype":{"type":"string"},"name":{"type":"string"},"amount":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}}}}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Tag several investment transactions","tags":["financial-data"]}},"/financial-data/account/{accountId}/transactions/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of one account's transactions (with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, an account the caller may not see returns 404.","operationId":"FinancialExportController_exportTransactions","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportTransactionsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export an account's transactions","tags":["financial-data-export"]}},"/financial-data/plaid-item/{plaidItemId}/transactions/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of the transactions of every account on the Plaid item (optionally narrowed by account type, with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, only accounts the caller may see are included (404 if none are left).","operationId":"FinancialExportController_exportPlaidItemTransactions","parameters":[{"name":"plaidItemId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportPlaidItemTransactionsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export a Plaid item's transactions","tags":["financial-data-export"]}},"/financial-data/account/{accountId}/recurring-transactions/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of one account's recurring transactions (with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, an account the caller may not see returns 404.","operationId":"FinancialExportController_exportRecurringTransactions","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportRecurringTransactionsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export an account's recurring transactions","tags":["financial-data-export"]}},"/financial-data/plaid-item/{plaidItemId}/recurring-transactions/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of the recurring transactions of every account on the Plaid item (optionally narrowed by account type, with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, only accounts the caller may see are included (404 if none are left).","operationId":"FinancialExportController_exportPlaidItemRecurringTransactions","parameters":[{"name":"plaidItemId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportPlaidItemRecurringTransactionsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export a Plaid item's recurring transactions","tags":["financial-data-export"]}},"/financial-data/account/{accountId}/holdings/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of one account's holdings (with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, an account the caller may not see returns 404.","operationId":"FinancialExportController_exportHoldings","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportHoldingsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export an account's holdings","tags":["financial-data-export"]}},"/financial-data/plaid-item/{plaidItemId}/holdings/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of the holdings of every account on the Plaid item (optionally narrowed by account type, with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, only accounts the caller may see are included (404 if none are left).","operationId":"FinancialExportController_exportPlaidItemHoldings","parameters":[{"name":"plaidItemId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportPlaidItemHoldingsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export a Plaid item's holdings","tags":["financial-data-export"]}},"/financial-data/account/{accountId}/liabilities/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of one account's liabilities (with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, an account the caller may not see returns 404.","operationId":"FinancialExportController_exportLiabilities","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportLiabilitiesDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export an account's liabilities","tags":["financial-data-export"]}},"/financial-data/plaid-item/{plaidItemId}/liabilities/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of the liabilities of every account on the Plaid item (optionally narrowed by account type, with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, only accounts the caller may see are included (404 if none are left).","operationId":"FinancialExportController_exportPlaidItemLiabilities","parameters":[{"name":"plaidItemId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportPlaidItemLiabilitiesDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export a Plaid item's liabilities","tags":["financial-data-export"]}},"/financial-data/account/{accountId}/investment-transactions/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of one account's investment transactions (with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, an account the caller may not see returns 404.","operationId":"FinancialExportController_exportAccountInvestmentTransactions","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportAccountInvestmentTransactionsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export an account's investment transactions","tags":["financial-data-export"]}},"/financial-data/plaid-item/{plaidItemId}/investment-transactions/export":{"post":{"description":"Queues a background job that builds a CSV or PDF of the investment transactions of every account on the Plaid item (optionally narrowed by account type, with the given filters) and returns its job id; poll GET /financial-data/export/:jobId for the download URL. On a case shared with opposing counsel, only accounts the caller may see are included (404 if none are left).","operationId":"FinancialExportController_exportInvestmentTransactions","parameters":[{"name":"plaidItemId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportInvestmentTransactionsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1","description":"Export job id. Poll GET /financial-data/export/:jobId for status and the download URL."}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Export a Plaid item's investment transactions","tags":["financial-data-export"]}},"/financial-data/case/{caseId}/exports":{"get":{"description":"Returns every financial export job on the case, from all users and including statement bundles, newest first; opposing counsel gets only the jobs it started. Finished jobs include a fresh download URL. Not paginated.","operationId":"FinancialExportController_listExportsByCaseId","parameters":[{"name":"caseId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ListFinancialExportJobsItemEntity"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a case's financial exports","tags":["financial-data-export"]}},"/financial-data/export/{jobId}":{"get":{"description":"Returns the status of one financial export job (statement bundles included). When completed it also returns the file name, completion time and a fresh download URL; when failed, the error message. Only the user who started the export can see it; anyone else gets 404.","operationId":"FinancialExportController_getExportJobStatus","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/FinancialExportJobResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get a financial export's status","tags":["financial-data-export"]}},"/financial-data/statements/{plaidItemId}":{"get":{"description":"Asks Plaid for the item's available statements, stores any new ones, and returns every stored statement record for the item (account, month, year, and the downloaded file once there is one). Returns 409 if the item's accounts have not synced yet.","operationId":"PlaidStatementsController_listStatements","parameters":[{"name":"plaidItemId","required":true,"in":"path","description":"MongoDB _id of the PlaidItem","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","description":"Stored statement records for the item, as saved in the database.","items":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"plaidItem":{"type":"string"},"financialAccount":{"type":"string","nullable":true},"statementId":{"type":"string"},"plaidAccountId":{"type":"string"},"month":{"type":"number","example":3},"year":{"type":"number","example":2026},"userFile":{"type":"string","nullable":true,"description":"Set once the PDF has been downloaded"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"__v":{"type":"number"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List a Plaid item's bank statements","tags":["financial-data"]}},"/financial-data/statements/{plaidItemId}/bulk-download":{"post":{"description":"Queues a background job that downloads every stored statement PDF for the item and zips them, and returns its job id; poll GET /financial-data/export/:jobId for the download URL. If a bundle job for the item is already running in this case, returns that job's id instead. Returns 400 if no statements are stored yet (list them first).","operationId":"PlaidStatementsController_bulkDownloadStatements","parameters":[{"name":"plaidItemId","required":true,"in":"path","description":"MongoDB _id of the PlaidItem","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDownloadStatementsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/BulkDownloadStatementsResponseEntity"},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Download all statements as a ZIP","tags":["financial-data"]}},"/financial-data/statements/{plaidItemId}/refresh":{"post":{"description":"Asks Plaid to re-fetch the last 24 months of statements, then does the same as listing: stores any new statements and returns every stored statement record for the item. Already-downloaded PDFs are untouched.","operationId":"PlaidStatementsController_refreshStatements","parameters":[{"name":"plaidItemId","required":true,"in":"path","description":"MongoDB _id of the PlaidItem","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","description":"Stored statement records for the item, as saved in the database.","items":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"plaidItem":{"type":"string"},"financialAccount":{"type":"string","nullable":true},"statementId":{"type":"string"},"plaidAccountId":{"type":"string"},"month":{"type":"number","example":3},"year":{"type":"number","example":2026},"userFile":{"type":"string","nullable":true,"description":"Set once the PDF has been downloaded"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"__v":{"type":"number"}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Refresh a Plaid item's statements","tags":["financial-data"]}},"/plaid-partner/customer":{"post":{"description":"Org admins only. Registers the organization as a Plaid end customer through Plaid's reseller partner API, saves the record and tries to enable it straight away, then returns the saved record (Plaid secrets are never included). Returns 409 if the organization is already registered, or outside production.","operationId":"PlaidPartnerController_createEndCustomer","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePartnerCustomerDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","description":"The stored Plaid end-customer record.","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"organization":{"type":"string"},"companyName":{"type":"string"},"legalEntityName":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"applicationName":{"type":"string","nullable":true},"endCustomerClientId":{"type":"string","nullable":true},"status":{"type":"string","enum":["under_review","pending_enablement","active","denied"]},"products":{"type":"array","items":{"type":"string"}},"plaidRequestId":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"__v":{"type":"number"}}},"statusCode":{"type":"number","example":201},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}},"409":{"description":"End Customer already exists"}},"security":[{"access-token":[]}],"summary":"Register the organization with Plaid","tags":["plaid-partner"]},"delete":{"description":"Org admins only. Removes the organization's end customer on Plaid and deletes the stored record. Returns 404 if the organization is not registered. Returns 409 outside production.","operationId":"PlaidPartnerController_removeEndCustomer","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Removed. No response body."}},"security":[{"access-token":[]}],"summary":"Remove the organization from Plaid","tags":["plaid-partner"]}},"/plaid-partner/customer/status":{"get":{"description":"Org admins only. Returns the stored onboarding status of the organization's Plaid end customer as a plain string, without calling Plaid. Returns 404 if the organization is not registered. Returns 409 outside production.","operationId":"PlaidPartnerController_getEndCustomerStatus","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"string","enum":["under_review","pending_enablement","active","denied"],"example":"active"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Get the organization's Plaid status","tags":["plaid-partner"]}},"/plaid-partner/customer/oauth-institutions":{"get":{"description":"Org admins only. Asks Plaid which OAuth banks the organization's end customer is registered with and returns Plaid's oauth_institutions list unchanged (empty if Plaid sends none). Returns 404 if the organization is not registered. Returns 409 outside production.","operationId":"PlaidPartnerController_getOAuthInstitutions","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","description":"Plaid's oauth_institutions list, passed through unchanged. Its shape is defined by Plaid, not by this API.","items":{"type":"object"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List OAuth bank registrations","tags":["plaid-partner"]}},"/plaid-partner/customer/webhooks/plaid":{"post":{"description":"Called by Plaid, no sign-in. When Plaid reports that an end customer's OAuth registration is approved, the matching organization's Plaid account is enabled; other events are ignored. Answers with received: true. The request must carry Plaid's `Plaid-Verification` signature; while the `plaid-webhook-verification` flag is on, a missing or invalid signature gets 401.","operationId":"PlaidPartnerController_handlePlaidWebhook","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"received":{"type":"boolean","example":true}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Receive a Plaid partner webhook (internal)","tags":["plaid-partner"],"x-internal":true}},"/.well-known/microsoft-identity-association.json":{"get":{"description":"Publishes the Microsoft application ids allowed to use this domain, for Microsoft's publisher domain verification. No auth; not wrapped in the envelope.","operationId":"WellKnownController_appleDeveloperMerchantidDomainAssociation","parameters":[],"responses":{"200":{"description":"The association file.","content":{"application/json":{"schema":{"type":"object","example":{"associatedApplications":[{"applicationId":"00000000-0000-0000-0000-000000000000"}]}}}}}},"summary":"Microsoft identity association (internal)","tags":["WellKnown"],"x-internal":true}},"/data-source":{"get":{"description":"Lists every data source Hearsay can collect from (for example iOS and Android devices, Gmail, Facebook exports), sorted by category. Reference data; the list rarely changes.","operationId":"DataSourceController_findAll","parameters":[{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MasterDataSourceEntity"}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List data sources","tags":["data-source"]}},"/health":{"get":{"description":"Infrastructure probe: checks outbound HTTP, the database, disk usage (below 90%) and heap memory (below 4 GB), and returns each result. No auth. Returns 503 as soon as one check fails.","operationId":"AppController_healthCheck","parameters":[],"responses":{"200":{"description":"The Health Check is successful","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"type":"object","properties":{"httpCheck":{"type":"object","example":{"status":"ok","info":{"google":{"status":"up"}},"error":{}}},"mongoCheck":{"type":"object","example":{"status":"ok","info":{"mongo":{"status":"up"}},"error":{}}},"diskCheck":{"type":"object","example":{"status":"ok","info":{"storage":{"status":"up"}},"error":{}}},"memoryCheck":{"type":"object","example":{"status":"ok","info":{"memory_heap":{"status":"up"}},"error":{}}}}},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}},"503":{"description":"The Health Check is not successful\n\nA check failed.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"error"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"},"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}}},"summary":"Health check (internal)","tags":["app"],"x-internal":true}},"/case/{caseId}/events":{"get":{"description":"Lists the case's event log (general, case-data and opposing-counsel events), newest first.","operationId":"CaseEventsController_listCaseEvents","parameters":[{"name":"caseId","required":true,"in":"path","description":"Id of the case","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"perPage","required":false,"in":"query","schema":{"maximum":100,"default":10,"type":"number"}},{"name":"skip","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"If skip/limit not provided, page/perPage will take precedence","schema":{"type":"number"}},{"name":"sortOrder","required":false,"in":"query","schema":{"default":"asc","type":"string","enum":["asc","desc"]}},{"name":"type","required":false,"in":"query","schema":{"type":"string","enum":["financial_connection_removed","oc_invited","oc_accepted","oc_member_added","oc_member_removed","oc_ec_created","oc_suggestion_created","oc_suggestion_updated","oc_suggestion_message","oc_suggestion_rejected","oc_ec_approved","oc_data_approved","oc_data_revoked","oc_code_released","oc_code_unreleased","member_invited","member_accepted","case_created","case_status_changed","extraction_code_created","extraction_code_updated","extraction_code_locked","custodian_updated","device_attached","data_uploaded","export_started","export_finished","export_errored","export_cancelled"]}},{"name":"category","required":false,"in":"query","schema":{"type":"string","enum":["general","case_data","oc"]}},{"name":"organization","in":"header","description":"id of the selected (active) organization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponseEntity"},{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseEventEntity"}}}}]},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List case events","tags":["case-events"]}},"/admin/exports/live":{"get":{"description":"Lists exports for diagnosing stalls, earliest started first, with progress counters, time since the last progress and live queue state. By default it shows running exports, cancellations from the last 15 minutes and, while queue operations are on, exports whose queue job an admin removed in the last hour; `status`, `startedBefore`, `caseId` and `organizationId` narrow it, and `limit` (default 25, at most 100) and `skip` page it. Queue state or case names that cannot be loaded come back as null instead of failing the request. Super admin only.","operationId":"ExportAdminController_listLive","parameters":[{"name":"status","required":false,"in":"query","description":"One status or several. Defaults to in-progress AND cancelled (the \"live\" view, which keeps a just-cancelled export visible) when omitted — see ExportAdminService.","schema":{"type":"array","items":{"type":"string","enum":["not-started","inprogress","completed","failed","cancelled"]}}},{"name":"startedBefore","required":false,"in":"query","description":"Only exports started before this instant (ISO 8601).","schema":{"format":"date-time","type":"string"}},{"name":"caseId","required":false,"in":"query","description":"Id of the case","schema":{"type":"string"}},{"name":"organizationId","required":false,"in":"query","description":"Id of the organization","schema":{"type":"string"}},{"name":"skip","required":false,"in":"query","schema":{"maximum":10000,"default":0,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"maximum":100,"default":25,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/LiveExportListResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"List live exports (internal)","tags":["admin"],"x-internal":true}},"/admin/exports/{id}/cancel":{"post":{"description":"Records a cancel request: an export that has not started is cancelled at once and its queue job removed, and a running export stops at its next conversation boundary. Repeating it on a cancelled export succeeds. Only conversation PDF exports can be cancelled; other exports and completed or failed ones return 400, an unknown export 404, and 403 while export cancellation is turned off. Super admin only.","operationId":"ExportAdminController_cancel","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the export to cancel","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/CancelExportResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Cancel an export (internal)","tags":["admin"],"x-internal":true}},"/admin/exports/{id}/resume":{"post":{"description":"Re-queues a failed, cancelled or stuck in-progress export so its next run reuses the conversations an earlier attempt finished, keeping its checkpoints and progress counters. Only conversation PDF exports can be resumed; other exports and other statuses return 400, an unknown export 404, and 403 while export resume is turned off. Super admin only.","operationId":"ExportAdminController_resume","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the export to resume","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ResumeExportResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Resume an export (internal)","tags":["admin"],"x-internal":true}},"/admin/exports/{id}/priority":{"post":{"description":"Moves a waiting export to the `top` or `bottom` of the export queue by removing and re-adding its job, which gives it a new queue job id (returned) and resets its retry count. An export with no live job or one a worker already picked up returns 400 and one with several queued jobs 409; if the re-add fails it returns 500 and the export has no queue job until it is re-run or resumed. Returns 404 for an unknown export and 403 while queue operations are turned off. Super admin only.","operationId":"ExportAdminController_reprioritize","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the export to move","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReprioritizeExportDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/ReprioritizeExportResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Move a queued export in the queue (internal)","tags":["admin"],"x-internal":true}},"/admin/exports/{id}/job":{"delete":{"description":"Takes the export's job out of the export queue and leaves the export not started; it is not cancelled and runs only once re-run or resumed. When no live job is found the response says so instead of failing. A job a worker already picked up returns 400, several queued jobs 409, an unreadable queue 500, an unknown export 404 and 403 while queue operations are turned off. Super admin only.","operationId":"ExportAdminController_dequeue","parameters":[{"name":"id","required":true,"in":"path","description":"Id of the export whose queue job should be removed","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","required":["data","statusCode","timestamp"],"properties":{"data":{"$ref":"#/components/schemas/DequeueExportResponseEntity"},"statusCode":{"type":"number","example":200},"timestamp":{"type":"string","format":"date-time","example":"2026-01-15T10:15:00.000Z"}}}}}}},"security":[{"access-token":[]}],"summary":"Remove a queued export's job (internal)","tags":["admin"],"x-internal":true}}},"info":{"title":"UseHearsay API","description":"Every HTTP response is wrapped as { data, statusCode, timestamp } unless the operation says otherwise. Authenticated requests send a bearer token and, where the operation lists it, an `organization` header.","version":"1.0","contact":{}},"tags":[{"name":"UseHearsay API","description":""}],"servers":[],"components":{"securitySchemes":{"access-token":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"UserFileEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"photo.heic"},"providerId":{"type":"string","example":"uploads/507f1f77bcf86cd799439011/photo.heic"},"convertedName":{"type":"string","example":"photo.jpg","nullable":true},"convertedProviderId":{"type":"string","example":"uploads/507f1f77bcf86cd799439011/photo.jpg"},"provider":{"type":"string","example":"s3"},"size":{"type":"number","description":"Size in bytes","example":204800},"transferState":{"type":"number","nullable":true,"example":null}},"required":["createdAt","updatedAt","id","name","providerId","convertedName","convertedProviderId","provider","size","transferState"]},"ActivePlanEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"subscription":{"type":"object","nullable":true,"example":null,"description":"Not loaded on public routes, so always null there. Only the internal admin organization report loads it."},"relatedSubscription":{"type":"array","items":{"type":"object"},"nullable":true,"example":[],"description":"Not loaded by any route, so always null or an empty list."},"organization":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"seats":{"type":"number","example":5},"seatsUsed":{"type":"number","example":3},"devices":{"type":"number","example":10},"deviceUsed":{"type":"number","example":4},"storage":{"type":"number","description":"Storage in bytes","example":100000000000},"storageUsed":{"type":"number","description":"Storage used in bytes","example":12000000000},"payments":{"type":"array","items":{"type":"object"},"nullable":true,"example":[],"description":"Not loaded by any route, so always null or an empty list."},"type":{"type":"string","enum":["admin","free","limited-access","trial","tier1","tier2","tier3","tier4","tier5","tier6","tier7"]},"isActive":{"type":"boolean","example":true},"isBetweenPlanChanges":{"type":"boolean","example":false},"storageInKb":{"type":"number","example":100000000},"storageUsedInKb":{"type":"number","example":12000000},"storageInMb":{"type":"number","example":100000},"storageUsedInMb":{"type":"number","example":12000},"storageInGb":{"type":"number","example":100},"storageUsedInGb":{"type":"number","example":12},"storageInTb":{"type":"number","example":0.1},"storageUsedInTb":{"type":"number","example":0.012},"isFreePlan":{"type":"boolean","example":false},"remainingSeats":{"type":"number","example":2},"remainingDevices":{"type":"number","example":6},"remainingStorage":{"type":"number","description":"Storage left in bytes (storage minus storageUsed).","example":88000000000}},"required":["createdAt","updatedAt","id","subscription","relatedSubscription","organization","seats","seatsUsed","devices","deviceUsed","storage","storageUsed","payments","type","isActive","isBetweenPlanChanges","storageInKb","storageUsedInKb","storageInMb","storageUsedInMb","storageInGb","storageUsedInGb","storageInTb","storageUsedInTb","isFreePlan","remainingSeats","remainingDevices","remainingStorage"]},"OrganizationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Example Law LLP"},"slug":{"type":"string","example":"example-law-llp"},"email":{"type":"string","example":"office@example.com"},"contact":{"type":"string","example":"Jane Doe"},"city":{"type":"string","example":"New York"},"state":{"type":"string","example":"NY"},"country":{"type":"string","example":"US"},"description":{"type":"string","example":"Litigation practice"},"phone":{"example":["+15555550100"],"type":"array","items":{"type":"string"}},"logo":{"type":"string","example":"https://files.example.com/logo.png","nullable":true},"isActive":{"type":"boolean","example":true},"isPriority":{"type":"boolean","example":false},"activePlan":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/ActivePlanEntity"}]},"metadata":{"type":"object","example":{}},"type":{"type":"string","example":"law_firm"},"version":{"type":"string","enum":["v1","v2","eu","ca"]}},"required":["createdAt","updatedAt","id","name","slug","email","contact","city","state","country","description","phone","logo","isActive","activePlan","metadata","type","version"]},"UserReferenceEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane.doe@example.com"},"phone":{"example":["+15555550100"],"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean","example":true},"isEmailVerified":{"type":"boolean","example":true},"lastActive":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"roles":{"example":["user"],"type":"array","items":{"type":"string"}},"avatar":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileEntity"}]},"onboardingStatus":{"type":"string","example":"completed","nullable":true},"userType":{"type":"string","example":"attorney","nullable":true},"firstName":{"type":"string","example":"Jane"},"lastName":{"type":"string","example":"Doe"},"initials":{"type":"string","example":"JD"},"userOrganizations":{"type":"array","items":{"type":"object"},"example":[],"description":"Only filled on the signed-in user's own profile; absent or empty on a user nested in another record."}},"required":["createdAt","updatedAt","id","name","email","phone","isActive","isEmailVerified","lastActive","roles","avatar","onboardingStatus","userType","firstName","lastName","initials"]},"UserOrgPermissionEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"user":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"role":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"],"example":"member"},"organization":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"lastActiveAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true}},"required":["createdAt","updatedAt","id","user","role","organization","lastActiveAt"]},"OrganizationBillingLimitsEntity":{"type":"object","properties":{"seatCap":{"type":"number","nullable":true,"description":"null = unlimited","example":10},"storageGB":{"type":"number","example":5000},"perCaseSourceCap":{"type":"number","nullable":true,"description":"Provisioned, not enforced — display only","example":null}},"required":["seatCap","storageGB","perCaseSourceCap"]},"OrganizationBillingPriceEntity":{"type":"object","properties":{"skuKey":{"type":"string","enum":["collection.phone","collection.social","collection.email","collection.ai-chat","collection.financial","collection.discord","collection.reddit","collection.linkedin","collection.legal","collection.file","collection.other","case.flat","case.pro-upgrade","addon.kit-rental","addon.laptop-rental","addon.laptop-rental-setup","addon.managed-service","addon.shipping","addon.live-bank","plan.base","source.per","ai.tokens-overage","adjustment.manual","adjustment.discount"],"example":"collection.phone"},"name":{"type":"string","example":"Phone collection"},"unitAmountCents":{"type":"number","description":"What this org is charged per unit, in cents","example":17900},"included":{"type":"boolean","description":"Bundled by the plan — charged at 0, not billed","example":false},"listAmountCents":{"type":"number","nullable":true,"description":"What an included item would otherwise have cost","example":null},"overridden":{"type":"boolean","description":"Set by this org's negotiated price rather than the plan's catalog rate","example":false},"recurrence":{"type":"string","enum":["monthly","semiannual"],"nullable":true,"description":"null for a one-time charge; otherwise how often it repeats","example":null},"freeFirstPeriod":{"type":"boolean","description":"First period of a recurring item costs 0","example":false}},"required":["skuKey","name","unitAmountCents","included","listAmountCents","overridden","recurrence","freeFirstPeriod"]},"OrganizationBillingEntity":{"type":"object","properties":{"tier":{"type":"string","enum":["public","solo","firm","firm-plus"],"example":"firm"},"tierName":{"type":"string","example":"Firm"},"billingMode":{"type":"string","enum":["payg","per-source","flat-case"],"example":"per-source"},"cadence":{"type":"string","enum":["monthly","annual"],"example":"monthly"},"baseFeeCents":{"type":"number","description":"Platform fee for one period at this cadence, in cents","example":24900},"paymentMethodOnFile":{"type":"boolean","example":true},"supportTier":{"type":"string","example":"next-day"},"limits":{"$ref":"#/components/schemas/OrganizationBillingLimitsEntity"},"features":{"example":["rsmf","ai","mcp","whiteLabel"],"description":"Feature keys enabled for this tier","type":"array","items":{"type":"string"}},"prices":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationBillingPriceEntity"}},"overrides":{"type":"object","description":"This org's negotiated exceptions, as stored. An absent SKU means the plan's catalog rate applies — read `prices` for what is actually charged.","example":{"skuUnitAmountCents":{"collection.phone":9900}}}},"required":["tier","tierName","billingMode","cadence","baseFeeCents","paymentMethodOnFile","supportTier","limits","features","prices","overrides"]},"OrganizationDetailsEntity":{"type":"object","properties":{"organization":{"$ref":"#/components/schemas/OrganizationEntity"},"permission":{"$ref":"#/components/schemas/UserOrgPermissionEntity"},"billing":{"nullable":true,"description":"The org's plan, limits and resolved prices. `null` when the org is billed by the legacy model — either it has not been migrated to case billing or the feature is off.","type":"object","allOf":[{"$ref":"#/components/schemas/OrganizationBillingEntity"}]}},"required":["organization","permission","billing"]},"UserEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane.doe@example.com"},"phone":{"example":["+15555550100"],"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean","example":true},"isEmailVerified":{"type":"boolean","example":true},"lastActive":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"roles":{"example":["user"],"type":"array","items":{"type":"string"}},"avatar":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileEntity"}]},"onboardingStatus":{"type":"string","example":"completed","nullable":true},"userType":{"type":"string","example":"attorney","nullable":true},"firstName":{"type":"string","example":"Jane"},"lastName":{"type":"string","example":"Doe"},"initials":{"type":"string","example":"JD"},"userOrganizations":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationDetailsEntity"}}},"required":["createdAt","updatedAt","id","name","email","phone","isActive","isEmailVerified","lastActive","roles","avatar","onboardingStatus","userType","firstName","lastName","initials"]},"LoginEmailDto":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password"]},"GenerateTokenResponseEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"token":{"type":"string","description":"The token value. Returned once at mint time only.","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NWExYjJjMyJ9.c2lnbmF0dXJl"},"validTill":{"format":"date-time","type":"string","example":"2027-01-15T10:15:00.000Z"},"scopes":{"type":"array","items":{"type":"string","enum":["cases:create","codes:create","exports:create"]}},"label":{"type":"string","example":"Intake sync"}},"required":["createdAt","updatedAt","id","token","validTill"]},"GenerateTokenDto":{"type":"object","properties":{"scopes":{"type":"array","description":"Automation scopes to grant. Presence of this field mints a scoped automation token; must be non-empty and a subset of the allowed scopes.","items":{"type":"string","enum":["cases:create","codes:create","exports:create"]}},"label":{"type":"string","description":"Human-readable label for the automation token (e.g. \"Zapier – intake\")."},"ttlDays":{"type":"number","description":"Token lifetime in days (automation tokens only, 1–400). Defaults to the configured automation TTL (~365).","minimum":1,"maximum":400}}},"AutomationTokenListEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"label":{"type":"string","example":"Intake automation"},"scopes":{"type":"array","example":["cases:create","codes:create"],"items":{"type":"string","enum":["cases:create","codes:create","exports:create"]}},"lastUsed":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"validTill":{"format":"date-time","type":"string","example":"2027-01-15T14:30:00.000Z"},"isRevoked":{"type":"boolean","example":false}},"required":["createdAt","updatedAt","id","scopes","isRevoked"]},"UpdateTokenScopesDto":{"type":"object","properties":{"scopes":{"type":"array","description":"New scope set for the token. Must be a subset of the allowed scopes. Empty array disables the token.","items":{"type":"string","enum":["cases:create","codes:create","exports:create"]}}},"required":["scopes"]},"RefreshTokenResponseEntity":{"type":"object","properties":{"accessToken":{"type":"string","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NWExYjJjMyJ9.c2lnbmF0dXJl","description":"New access token. Send it as `Authorization: Bearer <token>`."},"refreshToken":{"type":"string","example":"b7c1e2f0a9d84c3e8f6a5b4c3d2e1f00","description":"The same refresh token that was sent; it keeps working until it expires."}},"required":["accessToken","refreshToken"]},"RefreshTokenDto":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"RegisterEmailDto":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"},"name":{"type":"string","maxLength":200,"minLength":3},"organizationName":{"type":"string"},"inviteEmails":{"type":"array","items":{"type":"array"}},"phone":{"type":"array","items":{"type":"string","maximum":20,"minimum":1}},"userType":{"type":"string"},"termsAccepted":{"type":"boolean"}},"required":["email","password","name","userType"]},"MessageResponseEntity":{"type":"object","properties":{"message":{"type":"string","example":"Done"},"isSuccess":{"type":"boolean","example":true}},"required":["message","isSuccess"]},"SendForgotPasswordLinkDto":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]},"ResetPasswordDto":{"type":"object","properties":{"email":{"type":"string"},"newPassword":{"type":"string"},"resetToken":{"type":"string"}},"required":["email","newPassword","resetToken"]},"EmailAvailabilityResponseEntity":{"type":"object","properties":{"isAvailable":{"type":"boolean","description":"Whether the email is available","example":true},"message":{"type":"string","description":"The message","example":"Email is available"},"type":{"type":"string","description":"Type of user (user/custodian)","example":"user"}},"required":["isAvailable","message","type"]},"CheckEmailAvailabilityDto":{"type":"object","properties":{"email":{"type":"string","description":"The email to check availability for","example":"test@example.com"}},"required":["email"]},"CaseProUpgradeResponseEntity":{"type":"object","properties":{"status":{"type":"string","example":"processing"}},"required":["status"]},"BillingSetupSessionResponseEntity":{"type":"object","properties":{"url":{"type":"string","example":"https://checkout.stripe.com/c/pay/cs_test_a1Example"}},"required":["url"]},"CreateBillingSetupSessionDto":{"type":"object","properties":{"successUrl":{"type":"string"},"cancelUrl":{"type":"string"}},"required":["successUrl","cancelUrl"]},"SelectedPlanResponseEntity":{"type":"object","properties":{"tier":{"type":"string","enum":["public","solo","firm","firm-plus"],"example":"firm"},"cadence":{"type":"string","enum":["monthly","annual"],"example":"monthly"},"billingMode":{"type":"string","enum":["payg","per-source","flat-case"],"example":"payg"}},"required":["tier","cadence","billingMode"]},"SelectPlanResponseEntity":{"type":"object","properties":{"plan":{"$ref":"#/components/schemas/SelectedPlanResponseEntity"},"paymentRequired":{"type":"boolean","example":true},"setupSessionUrl":{"type":"string","example":"https://checkout.stripe.com/c/pay/cs_test_a1Example"},"prorationCents":{"type":"number","description":"Net cents charged (positive) or credited (negative) now to reconcile the base fee on a plan change; 0 on a first-time plan selection.","example":15000}},"required":["plan","paymentRequired","prorationCents"]},"SelectPlanDto":{"type":"object","properties":{"tier":{"type":"string","enum":["public","solo","firm","firm-plus"]},"cadence":{"type":"string","enum":["monthly","annual"]},"successUrl":{"type":"string"},"cancelUrl":{"type":"string"}},"required":["tier"]},"CaseStatementRowResponseEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e4"},"skuKey":{"type":"string","example":"collection.phone"},"description":{"type":"string","example":"Phone collection"},"quantity":{"type":"number","example":1},"amountCents":{"type":"number","example":17900},"informational":{"type":"boolean","example":false},"listAmountCents":{"type":"number","nullable":true,"example":17900},"status":{"type":"string","enum":["DRAFT","PENDING","PROCESSING","INVOICED","PAID","FAILED","REFUNDED","COMPED","VOID"],"example":"INVOICED"},"hostedInvoiceUrl":{"type":"string","nullable":true,"example":"https://invoice.stripe.com/i/acct_Example/test_Example"},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"}},"required":["id","skuKey","description","quantity","amountCents","informational","listAmountCents","status","hostedInvoiceUrl","createdAt"]},"CaseStatementResponseEntity":{"type":"object","properties":{"caseId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"rows":{"type":"array","items":{"$ref":"#/components/schemas/CaseStatementRowResponseEntity"}},"totalChargedCents":{"type":"number","example":17900},"totalInformationalCents":{"type":"number","example":5000}},"required":["caseId","rows","totalChargedCents","totalInformationalCents"]},"OrgReconciliationRowResponseEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e4"},"skuKey":{"type":"string","example":"collection.phone"},"description":{"type":"string","example":"Phone collection"},"quantity":{"type":"number","example":1},"amountCents":{"type":"number","example":17900},"informational":{"type":"boolean","example":false},"listAmountCents":{"type":"number","nullable":true,"example":17900},"status":{"type":"string","enum":["DRAFT","PENDING","PROCESSING","INVOICED","PAID","FAILED","REFUNDED","COMPED","VOID"],"example":"INVOICED"},"hostedInvoiceUrl":{"type":"string","nullable":true,"example":"https://invoice.stripe.com/i/acct_Example/test_Example"},"invoiceId":{"type":"string","nullable":true,"example":"in_1PExample"},"chargeScope":{"type":"string","enum":["case","platform"],"description":"'case' = a charge for work on a specific case; 'platform' = an org-wide charge such as the plan base fee or storage overage. Stripe bills the two on separate invoices.","example":"case"},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"}},"required":["id","skuKey","description","quantity","amountCents","informational","listAmountCents","status","hostedInvoiceUrl","invoiceId","chargeScope","createdAt"]},"OrgReconciliationGroupResponseEntity":{"type":"object","properties":{"caseId":{"type":"string","description":"The case id, or the literal 'organization' for the org-wide group. Prefer `chargeScope` over comparing against that literal.","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"chargeScope":{"type":"string","enum":["case","platform"],"description":"Scope of every row in this group — the whole group is one or the other, because invoices are grouped the same way.","example":"case"},"rows":{"type":"array","items":{"$ref":"#/components/schemas/OrgReconciliationRowResponseEntity"}},"subtotalChargedCents":{"type":"number","example":17900}},"required":["caseId","chargeScope","rows","subtotalChargedCents"]},"OrgReconciliationResponseEntity":{"type":"object","properties":{"groups":{"type":"array","items":{"$ref":"#/components/schemas/OrgReconciliationGroupResponseEntity"}}},"required":["groups"]},"BillingHistoryResponseEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e5"},"type":{"type":"string","enum":["subscribed","plan_upgraded","plan_canceled","payment_succeeded","payment_failed","grace_started","grace_recovered","card_added","card_removed","extraction_code_paid","charge_recorded","invoice_created","invoice_paid","invoice_failed","refunded","dispute_created","case_pro_upgraded","tokens_granted","admin_draft","admin_draft_ready","admin_comp","admin_void","admin_profile_updated","migrated_to_case_billing","credit_granted","credit_deducted","credit_reversed","addon_activated","addon_canceled"],"example":"charge_recorded"},"category":{"type":"string","enum":["subscription","invoicing","payment_method","case_billing","admin"],"example":"case_billing"},"description":{"type":"string","example":"Charge recorded: Phone collection"},"case":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e2"},"createdBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e6"},"metadata":{"type":"object","example":{"skuKey":"collection.phone","amountCents":17900}},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"}},"required":["id","type","category","description","case","createdBy","metadata","createdAt"]},"PlanOptionResponseEntity":{"type":"object","properties":{"tier":{"type":"string","enum":["public","solo","firm","firm-plus"],"example":"firm"},"name":{"type":"string","example":"Firm"},"monthlyCents":{"type":"number","example":24900},"annualCents":{"type":"number","example":268920},"seatCap":{"type":"number","nullable":true,"example":null},"storageGB":{"type":"number","example":5000},"features":{"example":["rsmf","ai"],"type":"array","items":{"type":"string"}},"annualOnly":{"type":"boolean","example":false},"cadenceOptions":{"type":"array","example":["monthly","annual"],"items":{"type":"string","enum":["monthly","annual"]}}},"required":["tier","name","monthlyCents","annualCents","seatCap","storageGB","features","annualOnly","cadenceOptions"]},"BillingPlansResponseEntity":{"type":"object","properties":{"plans":{"type":"array","items":{"$ref":"#/components/schemas/PlanOptionResponseEntity"}}},"required":["plans"]},"PaymentMethodResponseEntity":{"type":"object","properties":{"brand":{"type":"string","example":"visa"},"last4":{"type":"string","example":"4242"},"expMonth":{"type":"number","example":4},"expYear":{"type":"number","example":2044}},"required":["brand","last4","expMonth","expYear"]},"CurrentPlanDetailsResponseEntity":{"type":"object","properties":{"tier":{"type":"string","enum":["public","solo","firm","firm-plus"],"example":"firm"},"cadence":{"type":"string","enum":["monthly","annual"],"example":"monthly"},"billingMode":{"type":"string","enum":["payg","per-source","flat-case"],"example":"payg"},"paymentMethodOnFile":{"type":"boolean","description":"Whether a usable card is on file, read live from Stripe rather than from the stored flag.","example":true},"paymentMethod":{"nullable":true,"description":"The card that will actually be charged (the customer default), or null when there is none.","type":"object","allOf":[{"$ref":"#/components/schemas/PaymentMethodResponseEntity"}]}},"required":["tier","cadence","billingMode","paymentMethodOnFile","paymentMethod"]},"UsageWithCapResponseEntity":{"type":"object","properties":{"used":{"type":"number","example":12},"cap":{"type":"number","nullable":true,"example":50}},"required":["used","cap"]},"UsageWithBudgetResponseEntity":{"type":"object","properties":{"used":{"type":"number","example":250000},"budget":{"type":"number","example":1000000}},"required":["used","budget"]},"CurrentPlanUsageResponseEntity":{"type":"object","properties":{"seats":{"$ref":"#/components/schemas/UsageWithCapResponseEntity"},"storageGB":{"$ref":"#/components/schemas/UsageWithCapResponseEntity"},"aiTokens":{"$ref":"#/components/schemas/UsageWithBudgetResponseEntity"},"cases":{"$ref":"#/components/schemas/UsageWithCapResponseEntity"}},"required":["seats","storageGB","aiTokens","cases"]},"BillingPlanCurrentResponseEntity":{"type":"object","properties":{"plan":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/CurrentPlanDetailsResponseEntity"}]},"usage":{"$ref":"#/components/schemas/CurrentPlanUsageResponseEntity"},"delinquent":{"type":"boolean","example":false}},"required":["plan","usage","delinquent"]},"CreditLedgerEntryResponseEntity":{"type":"object","properties":{"type":{"type":"string","enum":["credit","debit","reversal"],"example":"debit"},"reason":{"type":"string","enum":["admin_grant","deduction","reversal"],"example":"deduction"},"deltaCents":{"type":"number","example":-5000},"balanceAfterCents":{"type":"number","example":20000},"description":{"type":"string","example":"Credit applied to invoice in_1QExampleInvoice0001"},"invoice":{"type":"string","nullable":true,"example":"in_1QExampleInvoice0001"},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"}},"required":["type","reason","deltaCents","balanceAfterCents","description","invoice","createdAt"]},"CreditBalanceResponseEntity":{"type":"object","properties":{"balanceCents":{"type":"number","example":20000},"currency":{"type":"string","example":"usd"},"effectiveDeductPercentage":{"type":"number","example":100},"entries":{"type":"array","items":{"$ref":"#/components/schemas/CreditLedgerEntryResponseEntity"}}},"required":["balanceCents","currency","effectiveDeductPercentage","entries"]},"ReportWindow":{"type":"object","properties":{"startDate":{"format":"date-time","type":"string","example":"2026-01-01T00:00:00.000Z"},"endDate":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"}},"required":["startDate","endDate"]},"ReportRow":{"type":"object","properties":{"date":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"},"skuKey":{"type":"string","enum":["collection.phone","collection.social","collection.email","collection.ai-chat","collection.financial","collection.discord","collection.reddit","collection.linkedin","collection.legal","collection.file","collection.other","case.flat","case.pro-upgrade","addon.kit-rental","addon.laptop-rental","addon.laptop-rental-setup","addon.managed-service","addon.shipping","addon.live-bank","plan.base","source.per","ai.tokens-overage","adjustment.manual","adjustment.discount"],"example":"collection.phone"},"description":{"type":"string","example":"Phone collection"},"quantity":{"type":"number","example":1},"unitAmountCents":{"type":"number","example":17900},"amountCents":{"type":"number","example":17900},"status":{"type":"string","example":"INVOICED"},"invoiceId":{"type":"string","nullable":true,"example":"in_1PExample"}},"required":["date","skuKey","description","quantity","unitAmountCents","amountCents","status","invoiceId"]},"CaseGroup":{"type":"object","properties":{"caseId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"caseName":{"type":"string","example":"Smith v. Jones"},"rows":{"type":"array","items":{"$ref":"#/components/schemas/ReportRow"}},"subtotalChargedCents":{"type":"number","example":17900},"subtotalInformationalCents":{"type":"number","example":0}},"required":["caseId","caseName","rows","subtotalChargedCents","subtotalInformationalCents"]},"OrgReport":{"type":"object","properties":{"organizationId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"organizationName":{"type":"string","example":"Acme Legal"},"tier":{"type":"string","nullable":true,"example":"firm"},"billingMode":{"type":"string","nullable":true,"example":"payg"},"onCaseBilling":{"type":"boolean","example":true},"window":{"$ref":"#/components/schemas/ReportWindow"},"cases":{"type":"array","items":{"$ref":"#/components/schemas/CaseGroup"}},"totalChargedCents":{"type":"number","example":42300},"totalInformationalCents":{"type":"number","example":5000},"totalNonChargeableCents":{"type":"number","example":0},"totalFailedCents":{"type":"number","example":0},"totalRefundedCents":{"type":"number","example":0}},"required":["organizationId","organizationName","tier","billingMode","onCaseBilling","window","cases","totalChargedCents","totalInformationalCents","totalNonChargeableCents","totalFailedCents","totalRefundedCents"]},"BillingReportResponseEntity":{"type":"object","properties":{"organizations":{"type":"array","items":{"$ref":"#/components/schemas/OrgReport"}},"grandTotalChargedCents":{"type":"number","example":42300}},"required":["organizations","grandTotalChargedCents"]},"BillingReportUserFileEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"name":{"type":"string","example":"billing-report-2026-01.pdf"},"mimeType":{"type":"string","example":"application/pdf"},"size":{"type":"number","example":184320}},"required":["id","name","mimeType","size"]},"BillingReportFileResponseEntity":{"type":"object","properties":{"userFile":{"$ref":"#/components/schemas/BillingReportUserFileEntity"},"downloadUrl":{"type":"string","description":"Presigned, time-limited download URL","example":"https://acme-user-data.s3.amazonaws.com/billing-report-2026-01.pdf?X-Amz-Signature=abc123"}},"required":["userFile","downloadUrl"]},"BillingReportQueryDto":{"type":"object","properties":{"caseIds":{"description":"Defaults to all cases","type":"array","items":{"type":"string"}},"startDate":{"format":"date-time","type":"string","description":"Defaults to the start of the organization's current billing cadence period"},"endDate":{"format":"date-time","type":"string","description":"Defaults to now"},"format":{"type":"string","enum":["json","pdf"],"default":"json"}}},"UpdateUserDto":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"minLength":3},"phone":{"type":"array","items":{"type":"string","maximum":20,"minimum":1}},"onboardingStatus":{"type":"string"},"lastOnboardingStep":{"type":"number"},"userType":{"type":"string"}},"required":["onboardingStatus","lastOnboardingStep","userType"]},"ChangePasswordDto":{"type":"object","properties":{"oldPassword":{"type":"string"},"newPassword":{"type":"string"}},"required":["oldPassword","newPassword"]},"VerifyEmailDto":{"type":"object","properties":{"token":{"type":"string"},"email":{"type":"string"}},"required":["token"]},"OrganizationWithoutPlanEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Example Law LLP"},"slug":{"type":"string","example":"example-law-llp"},"email":{"type":"string","example":"office@example.com"},"contact":{"type":"string","example":"Jane Doe"},"city":{"type":"string","example":"New York"},"state":{"type":"string","example":"NY"},"country":{"type":"string","example":"US"},"description":{"type":"string","example":"Litigation practice"},"phone":{"example":["+15555550100"],"type":"array","items":{"type":"string"}},"logo":{"type":"string","example":"https://files.example.com/logo.png","nullable":true},"isActive":{"type":"boolean","example":true},"isPriority":{"type":"boolean","example":false},"activePlan":{"type":"object","nullable":true,"description":"Not loaded on this route, so always null.","example":null},"metadata":{"type":"object","example":{}},"type":{"type":"string","example":"law_firm"},"version":{"type":"string","enum":["v1","v2","eu","ca"]}},"required":["createdAt","updatedAt","id","name","slug","email","contact","city","state","country","description","phone","logo","isActive","activePlan","metadata","type","version"]},"OrganizationPermissionEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"user":{"type":"object","nullable":true,"description":"Not loaded on this route, so always null.","example":null},"role":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"],"example":"member"},"organization":{"type":"object","nullable":true,"example":null,"description":"Not loaded on this route, so always null."},"lastActiveAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true}},"required":["createdAt","updatedAt","id","user","role","organization","lastActiveAt"]},"OrganizationDetailsResponseEntity":{"type":"object","properties":{"organization":{"$ref":"#/components/schemas/OrganizationWithoutPlanEntity"},"permission":{"$ref":"#/components/schemas/OrganizationPermissionEntity"},"billing":{"nullable":true,"description":"The org's plan, limits and resolved prices. `null` when the org is billed by the legacy model — either it has not been migrated to case billing or the feature is off.","type":"object","allOf":[{"$ref":"#/components/schemas/OrganizationBillingEntity"}]}},"required":["organization","permission","billing"]},"CreateOrganizationDto":{"type":"object","properties":{"name":{"type":"string"},"website":{"type":"string"},"logo":{"type":"string"},"contact":{"type":"string"},"addressLine1":{"type":"string"},"addressLine2":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"country":{"type":"string"},"zipCode":{"type":"string"},"email":{"type":"string"},"description":{"type":"string"},"phone":{"type":"array","items":{"type":"string","maximum":20,"minimum":1}},"inviteEmails":{"type":"array","items":{"type":"array"}}},"required":["name"]},"InvitationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"invitedUserEmail":{"type":"string","example":"jane.doe@example.com"},"invitedUserName":{"type":"string","example":"Jane Doe"},"invitedUserPhone":{"description":"Phone numbers stored on the invitation. No invite flow fills it today, so it is an empty list.","example":["+15555550100"],"type":"array","items":{"type":"string"}},"invitedBy":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"organization":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/OrganizationEntity"}]},"invitedUserRole":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"],"example":"member"},"status":{"type":"string","enum":["pending","accepted","rejected"],"example":"pending"},"emailSent":{"type":"boolean","example":false},"emailSentAt":{"format":"date-time","type":"string","nullable":true,"example":null},"acceptedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"acceptedBy":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"invitedUser":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"rejectedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"rejectedReason":{"type":"string","example":"Already joined another organization.","nullable":true},"initials":{"type":"string","example":"JD"}},"required":["createdAt","updatedAt","id","invitedUserEmail","invitedUserName","invitedUserPhone","invitedBy","organization","invitedUserRole","status","emailSent","emailSentAt","acceptedAt","acceptedBy","invitedUser","rejectedAt","rejectedReason","initials"]},"InvitationListEntity":{"type":"object","properties":{"total":{"type":"number","example":42},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20},"totalPages":{"type":"number","example":3},"nextPage":{"type":"number","example":2,"nullable":true},"previousPage":{"type":"number","example":null,"nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/InvitationEntity"}}},"required":["total","totalPages","nextPage","previousPage","data"]},"OrganizationNameValidateResponseEntity":{"type":"object","properties":{"isValid":{"type":"boolean","example":true}},"required":["isValid"]},"ValidateOrganizationNameDto":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]},"CommunicationPreferencesDto":{"type":"object","properties":{"contactAllowed":{"type":"boolean"},"emailOk":{"type":"boolean"},"smsOk":{"type":"boolean"},"callOk":{"type":"boolean"},"maxTouchesPerWeek":{"type":"number"},"minHoursBetween":{"type":"number"},"quietHoursStart":{"type":"number"},"quietHoursEnd":{"type":"number"},"preferredTone":{"type":"string"},"escalateToFirmOk":{"type":"boolean"},"firmContactEmail":{"type":"string"},"equipmentAutoApproved":{"type":"boolean"},"managedUpgradeAllowed":{"type":"boolean"},"notes":{"type":"string"}}},"UpdateOrganizationDto":{"type":"object","properties":{"name":{"type":"string"},"website":{"type":"string"},"logo":{"type":"string"},"contact":{"type":"string"},"addressLine1":{"type":"string"},"addressLine2":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"country":{"type":"string"},"zipCode":{"type":"string"},"email":{"type":"string"},"description":{"type":"string"},"phone":{"type":"array","items":{"type":"string","maximum":20,"minimum":1}},"originData":{"type":"array","items":{"type":"string"}},"metadata":{"type":"object"},"acceptPlaidTerms":{"type":"boolean"},"communicationPreferences":{"$ref":"#/components/schemas/CommunicationPreferencesDto"}}},"OrganizationUsagesStatEntity":{"type":"object","properties":{"isActive":{"type":"boolean","example":true},"billingModel":{"type":"string","enum":["legacy","case-billing"],"description":"Which billing system the org is on. On `case-billing` there is no seat/device allowance: `seats`/`devices` are -1 (unlimited) and clients must not gate work on them.","example":"legacy"},"isPaidPlan":{"type":"boolean","example":true},"isPayAsYouGo":{"type":"boolean","example":false},"seats":{"type":"number","example":10},"seatsUsed":{"type":"number","example":4},"devices":{"type":"number","example":20},"deviceUsed":{"type":"number","example":7},"storage":{"type":"number","description":"Storage in bytes","example":100000000000},"storageUsed":{"type":"number","description":"Storage used in bytes","example":25000000000},"organizationName":{"type":"string","example":"Acme Law LLP"},"organizationSlug":{"type":"string","example":"acme-law-llp"},"organizationId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"hasUserAccessToBilling":{"type":"boolean","example":true},"isNewDevice":{"type":"boolean","example":true},"clientPayDetails":{"type":"object","nullable":true,"example":null},"canAddFreeDevice":{"type":"boolean","example":true},"canAddFreeSeats":{"type":"boolean","example":true},"storageInKb":{"type":"number","example":100000000},"storageUsedInKb":{"type":"number","example":25000000},"storageInMb":{"type":"number","example":100000},"storageUsedInMb":{"type":"number","example":25000},"storageInGb":{"type":"number","example":100},"storageUsedInGb":{"type":"number","example":25},"storageInTb":{"type":"number","example":0.1},"storageUsedInTb":{"type":"number","example":0.025}},"required":["isActive","billingModel","isPaidPlan","isPayAsYouGo","seats","seatsUsed","devices","deviceUsed","storage","storageUsed","organizationName","organizationSlug","organizationId","hasUserAccessToBilling","isNewDevice","canAddFreeDevice","canAddFreeSeats","storageInKb","storageUsedInKb","storageInMb","storageUsedInMb","storageInGb","storageUsedInGb","storageInTb","storageUsedInTb"]},"OrganizationStorageStatsCaseEntity":{"type":"object","properties":{"caseId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"caseName":{"type":"string","example":"Smith v. Jones"},"caseCreatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"usagesInBytes":{"type":"number","example":52428800}},"required":["caseId","caseName","caseCreatedAt","usagesInBytes"]},"OrganizationStorageStatsEntity":{"type":"object","properties":{"totalOrgUsagesInBytes":{"type":"number","example":1073741824},"cases":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationStorageStatsCaseEntity"}}},"required":["totalOrgUsagesInBytes","cases"]},"OrganizationListEntity":{"type":"object","properties":{"total":{"type":"number","example":42},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20},"totalPages":{"type":"number","example":3},"nextPage":{"type":"number","example":2,"nullable":true},"previousPage":{"type":"number","example":null,"nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationDetailsEntity"}}},"required":["total","totalPages","nextPage","previousPage","data"]},"OrganizationMembersListEntity":{"type":"object","properties":{"total":{"type":"number","example":42},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20},"totalPages":{"type":"number","example":3},"nextPage":{"type":"number","example":2,"nullable":true},"previousPage":{"type":"number","example":null,"nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/UserOrgPermissionEntity"}}},"required":["total","totalPages","nextPage","previousPage","data"]},"InviteOrganizationUser":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string","minLength":1,"maxLength":200},"invitedUserRole":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"],"default":"member"},"sendInstructions":{"type":"boolean","default":false}},"required":["email"]},"InviteOrganizationUsersDto":{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/InviteOrganizationUser"}}},"required":["users"]},"AppConfigDeviceSettingsEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"systemId":{"type":"string","example":"3f8e1c5a9b2d4e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f"},"deviceId":{"type":"string","example":"00008110-001A2B3C4D5E"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"backupPaths":{"example":["/Users/jane/HearsayBackups"],"type":"array","items":{"type":"string"}},"defaultBackupPath":{"type":"string","example":"/Users/jane/HearsayBackups"},"lastUsedBackupPath":{"type":"string","example":"/Users/jane/HearsayBackups"},"device":{"type":"string","nullable":true,"description":"Id of the device these settings belong to","example":"507f1f77bcf86cd799439011"},"lastBackupAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"lastLocalBackupAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"autoBackupFrequency":{"type":"string","enum":["none","daily","weekly","monthly"],"default":"none"},"deviceMetadata":{"type":"object","example":{"name":"Jane Doe's iPhone","productType":"iPhone15,2","productVersion":"17.4.1","timeZone":"America/Chicago"}},"otherSettings":{"type":"object","example":{}}},"required":["createdAt","updatedAt","id","systemId","deviceId","deviceType","backupPaths","defaultBackupPath","lastUsedBackupPath","device","lastBackupAt","lastLocalBackupAt","autoBackupFrequency","deviceMetadata","otherSettings"]},"OSDetailsEntity":{"type":"object","properties":{"arch":{"type":"string","example":"arm64"},"platform":{"type":"string","example":"darwin"},"version":{"type":"string","example":"14.4"},"type":{"type":"string","description":"Never sent today: nothing fills it (the OS type is stored and sent as osType).","example":"Darwin"},"osType":{"type":"string","description":"Operating system type reported by the desktop app.","example":"Darwin"},"machine":{"type":"string","example":"arm64"},"totalMemory":{"type":"number","example":17179869184},"cpus":{"type":"object","example":{}},"extraInformation":{"type":"object","example":{"cpu":{"manufacturer":"Apple","brand":"M2","physicalCores":8},"mem":{"total":17179869184},"osInfo":{"platform":"darwin","distro":"macOS","release":"14.4","arch":"arm64"},"battery":{"hasBattery":true,"percent":80}}}}},"AppConfigEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"systemId":{"type":"string","example":"3f8e1c5a9b2d4e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f"},"deviceSettings":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/AppConfigDeviceSettingsEntity"}},"defaultBackupPath":{"type":"string","example":"/Users/jane/HearsayBackups"},"lastUsedBackupPath":{"type":"string","example":"/Users/jane/HearsayBackups"},"otherSettings":{"type":"object","example":{}},"osDetails":{"$ref":"#/components/schemas/OSDetailsEntity"},"lastAppUsedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"lastUsedAppVersion":{"type":"string","example":"2.1.15"},"defaultSettings":{"type":"object","example":{"contactAdminDeviceRequestText":"Please add another device to my account."},"nullable":true}},"required":["createdAt","updatedAt","id","systemId","deviceSettings","defaultBackupPath","lastUsedBackupPath","otherSettings","osDetails","lastAppUsedAt","lastUsedAppVersion","defaultSettings"]},"ExtractionCodeConfigDateRangeEntity":{"type":"object","properties":{"start":{"format":"date-time","type":"string","nullable":true,"example":null},"end":{"format":"date-time","type":"string","nullable":true,"example":null}},"required":["start","end"]},"ExtractionCodeConfigAppConfigConcurrencyEntity":{"type":"object","properties":{"mediaType":{"type":"string","enum":["images","video","audio","documents"]},"concurrency":{"example":[4,6,12,16],"type":"array","items":{"type":"number"}}},"required":["mediaType","concurrency"]},"ExtractionCodeConfigAppConfigEntity":{"type":"object","properties":{"conversionConcurrency":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeConfigAppConfigConcurrencyEntity"}},"compressionConcurrency":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeConfigAppConfigConcurrencyEntity"}},"enableLocalMediaConversion":{"type":"boolean","example":true},"enableLocalMediaCompression":{"type":"boolean","example":true},"enableIosEncryptedBackupExtraction":{"type":"boolean","example":false},"allowOnlyEncryptedIosBackup":{"type":"boolean","example":false},"autoIncludeSafariData":{"type":"boolean","example":false},"autoIncludeNotesData":{"type":"boolean","example":false},"autoIncludeContacts":{"type":"boolean","default":false,"example":false},"shareAction":{"type":"string","enum":["share","export","share_and_export"],"default":"share"}},"required":["conversionConcurrency","compressionConcurrency"]},"ExtractionCodeConfigEnableLocalExportDefaultConfigEntity":{"type":"object","properties":{"chunkType":{"type":"string","enum":["year","month","week","day","none"]},"includeAttachments":{"type":"boolean","example":true}},"required":["chunkType","includeAttachments"]},"ExtractionCodeConfigEnableLocalExportEntity":{"type":"object","properties":{"formats":{"example":["pdf"],"type":"array","items":{"type":"string"}},"defaultConfig":{"$ref":"#/components/schemas/ExtractionCodeConfigEnableLocalExportDefaultConfigEntity"}}},"ExtractionCodeConfigAllowClientEntity":{"type":"object","properties":{"allowSocialApps":{"example":["whatsapp"],"type":"array","items":{"type":"string"}},"allowShareAll":{"type":"boolean","example":false}},"required":["allowSocialApps","allowShareAll"]},"ExtractionCodeConfigAutoShareSearchesEntity":{"type":"object","properties":{"type":{"type":"string","enum":["name","phone","keywords","email"],"default":"name"},"values":{"example":["Jane Doe"],"type":"array","items":{"type":"string"}},"apps":{"example":["whatsapp"],"type":"array","items":{"type":"string"}},"dateRange":{"$ref":"#/components/schemas/ExtractionCodeConfigDateRangeEntity"},"exactMatch":{"type":"boolean","example":false},"include":{"type":"boolean","example":true},"conversationType":{"type":"string","enum":["all","dm","group"],"default":"all"}},"required":["type","values","apps","dateRange","exactMatch","include","conversationType"]},"ExtractionCodeConfigAutoShareEntity":{"type":"object","properties":{"allowClientApproval":{"type":"boolean","example":false},"allowAdditionalShare":{"type":"boolean","example":false},"searches":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeConfigAutoShareSearchesEntity"}}},"required":["allowClientApproval","allowAdditionalShare","searches"]},"ExtractionCodeConfigShareAllSearchesExcludeEntity":{"type":"object","properties":{"type":{"type":"string","enum":["name","phone","keywords","email"],"default":"name"},"values":{"example":["Jane Doe"],"type":"array","items":{"type":"string"}},"apps":{"example":["whatsapp"],"type":"array","items":{"type":"string"}},"exactMatch":{"type":"boolean","example":false},"conversationType":{"type":"string","enum":["all","dm","group"],"default":"all"}},"required":["type","values","apps","exactMatch","conversationType"]},"ExtractionCodeConfigShareAllEntity":{"type":"object","properties":{"apps":{"example":["whatsapp"],"type":"array","items":{"type":"string"}},"includeAttachments":{"type":"boolean","example":true},"dateRange":{"$ref":"#/components/schemas/ExtractionCodeConfigDateRangeEntity"},"searchesExclude":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeConfigShareAllSearchesExcludeEntity"}}},"required":["apps","includeAttachments"]},"ExtractionCodeConfigEntity":{"type":"object","properties":{"type":{"type":"string","enum":["allow_client","share_all","auto_share"],"default":"allow_client"},"origin":{"type":"string","example":"Website","nullable":true},"ignoreMediaPermissionAndroid":{"type":"boolean","example":false},"dateRange":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/ExtractionCodeConfigDateRangeEntity"}]},"appConfig":{"$ref":"#/components/schemas/ExtractionCodeConfigAppConfigEntity"},"enableLocalExport":{"$ref":"#/components/schemas/ExtractionCodeConfigEnableLocalExportEntity"},"allowClient":{"$ref":"#/components/schemas/ExtractionCodeConfigAllowClientEntity"},"autoShare":{"$ref":"#/components/schemas/ExtractionCodeConfigAutoShareEntity"},"shareAll":{"$ref":"#/components/schemas/ExtractionCodeConfigShareAllEntity"}},"required":["type","origin"]},"MasterDataSourceEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"gmail"},"displayName":{"type":"string","example":"Gmail"},"category":{"type":"string","example":"email"},"config":{"type":"object","description":"Collection options for this source. The keys below are the ones the code reads, for device sources (ios, android).","properties":{"iosEnableEncryptedBackup":{"type":"boolean"},"iosAllowOnlyEncryptedBackup":{"type":"boolean"},"iosAutoIncludeSafariData":{"type":"boolean"},"iosAutoIncludeNotesData":{"type":"boolean"},"androidIgnoreMediaPermission":{"type":"boolean"},"iosAllApps":{"type":"array","items":{"type":"string"}},"iosAllowedApps":{"type":"array","items":{"type":"string"}},"androidAllApps":{"type":"array","items":{"type":"string"}},"androidAllowedApps":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"example":{"iosEnableEncryptedBackup":true,"iosAllowOnlyEncryptedBackup":false,"iosAllApps":["messages","call_log","whatsapp","photos"],"iosAllowedApps":["messages","call_log"]},"nullable":true}},"required":["createdAt","updatedAt","id","name","displayName","category","config"]},"CaseDataSourceEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"dataSource":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/MasterDataSourceEntity"}]},"config":{"type":"object","example":{"iosEnableEncryptedBackup":true,"iosAllowOnlyEncryptedBackup":false,"iosAutoIncludeSafariData":true,"iosAutoIncludeNotesData":false,"iosAllowedApps":["messages","call_log","whatsapp"]},"nullable":true}},"required":["createdAt","updatedAt","id","case","dataSource","config"]},"ExtractionCodeDataSourceEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"extractionCode":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"dataSource":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/MasterDataSourceEntity"}]},"caseDataSource":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/CaseDataSourceEntity"}]},"config":{"type":"object","example":{"iosEnableEncryptedBackup":true,"iosAllowOnlyEncryptedBackup":false,"iosAllApps":["messages","call_log","whatsapp","photos"],"iosAllowedApps":["messages","call_log"]},"nullable":true}},"required":["createdAt","updatedAt","id","case","extractionCode","dataSource","caseDataSource","config"]},"ExtractionCodeEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"code":{"type":"string","example":"K7Q2-9XPL"},"email":{"type":"string","example":"jane.doe@example.com"},"name":{"type":"string","example":"Jane Doe","nullable":true},"phone":{"type":"string","nullable":true,"example":"+15555550100"},"isActive":{"type":"boolean","example":true},"label":{"type":"string","example":"Primary phone","nullable":true},"config":{"$ref":"#/components/schemas/ExtractionCodeConfigEntity"},"devices":{"type":"array","items":{"type":"object"},"nullable":true,"example":[],"description":"Not loaded by any route, so always null or an empty list."},"totalDevicesCount":{"type":"number","example":1},"dueDate":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"sendEmail":{"type":"boolean","default":false,"example":true},"lockedAt":{"format":"date-time","type":"string","example":null,"nullable":true},"hasDueDateCrossed":{"type":"boolean","example":false},"status":{"type":"string","example":"pending"},"reviewStatus":{"type":"string","enum":["approved","rejected","not_reviewed","na"],"nullable":true},"reviewMessage":{"type":"string","nullable":true,"example":null},"lastApprovedBy":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"ocReviewedByOrg":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/OrganizationEntity"}]},"ocReviewedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"ocRequired":{"type":"boolean","default":true,"example":true},"allowShareAll":{"type":"boolean","deprecated":true,"example":false},"metadata":{"type":"object","example":{}},"logsConfig":{"type":"object","example":{}},"sources":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeDataSourceEntity"}},"payment":{"type":"object","example":null,"nullable":true}},"required":["createdAt","updatedAt","id","code","email","name","phone","isActive","label","config","devices","totalDevicesCount","hasDueDateCrossed","status","reviewStatus","reviewMessage","lastApprovedBy","ocReviewedByOrg","ocReviewedAt","ocRequired","allowShareAll","metadata","logsConfig","payment"]},"ExtractionLoginCaseEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Doe v. Example Corp"},"slug":{"type":"string","example":"doe-v-example-corp"},"description":{"type":"string","example":"Employment dispute","nullable":true},"matterId":{"type":"string","nullable":true,"example":"MAT-0042"},"status":{"type":"string","enum":["open","closed","archived","pending"],"example":"open"},"assignStatus":{"type":"string","enum":["assigned","unassigned"],"example":"assigned"},"createdBy":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"assignedTo":{"description":"Assigned user ids","example":["507f1f77bcf86cd799439011"],"type":"array","items":{"type":"string"}},"organization":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/OrganizationEntity"}]},"isActive":{"type":"boolean","example":true},"priority":{"type":"string","enum":["low","medium","high","urgent"],"example":"medium"},"attachedDevices":{"description":"Attached device ids","example":["507f1f77bcf86cd799439012"],"type":"array","items":{"type":"string"}},"totalConversations":{"type":"number","example":128},"archivedBy":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"exportJobId":{"type":"string","example":"job-1234","nullable":true},"metadata":{"type":"object","example":{}},"extractionCodePaymentThreadId":{"type":"string","nullable":true,"example":null},"ocCase":{"type":"boolean","example":false},"isOcShared":{"type":"boolean","example":false},"proEnabled":{"type":"boolean","example":false},"isArchived":{"type":"boolean","example":false}},"required":["createdAt","updatedAt","id","name","slug","description","status","assignStatus","createdBy","assignedTo","organization","isActive","priority","attachedDevices","totalConversations","archivedBy","exportJobId","metadata","extractionCodePaymentThreadId","ocCase","isOcShared","proEnabled","isArchived"]},"LoginResponseEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"accessToken":{"type":"string","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NWExYjJjMyJ9.c2lnbmF0dXJl"},"refreshToken":{"type":"string","example":"b7c1e2f0a9d84c3e8f6a5b4c3d2e1f00"},"userProfile":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/UserEntity"}]},"appConfig":{"$ref":"#/components/schemas/AppConfigEntity"},"extractionCodeDetails":{"$ref":"#/components/schemas/ExtractionCodeEntity"},"caseDetails":{"description":"Sent only by the extraction-code login","allOf":[{"$ref":"#/components/schemas/ExtractionLoginCaseEntity"}]},"collectionEventsEnabled":{"type":"boolean","description":"Desktop/portal should send collection events and heartbeats","example":true}},"required":["createdAt","updatedAt","id","accessToken","refreshToken","userProfile"]},"InvitationAcceptResponseEntity":{"type":"object","properties":{"login":{"$ref":"#/components/schemas/LoginResponseEntity"},"organization":{"$ref":"#/components/schemas/OrganizationDetailsResponseEntity"}},"required":["organization"]},"AcceptOrganizationInvitationRequestDto":{"type":"object","properties":{"token":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"password":{"type":"string"}},"required":["token","email"]},"RejectOrganizationInvitationRequestDto":{"type":"object","properties":{"token":{"type":"string"},"email":{"type":"string"},"reason":{"type":"string"}},"required":["token","email"]},"UpdateOrganizationUserAccess":{"type":"object","properties":{"id":{"type":"string"},"accessLevel":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"],"default":"member"}},"required":["id","accessLevel"]},"UpdateOrganizationUserAccessDto":{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/UpdateOrganizationUserAccess"}}},"required":["users"]},"UpdateOrganizationUser":{"type":"object","properties":{"id":{"type":"string"},"role":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"]},"name":{"type":"string","maxLength":200,"minLength":3},"email":{"type":"string","maxLength":100}},"required":["id"]},"UpdateOrganizationUsersDto":{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/UpdateOrganizationUser"}}},"required":["users"]},"DeviceRequestContactAdminDto":{"type":"object","properties":{"contactAdminDeviceRequestText":{"type":"string"},"caseId":{"type":"string"}}},"PaginatedResponseEntity":{"type":"object","properties":{"total":{"type":"number","example":42},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20},"totalPages":{"type":"number","example":3},"nextPage":{"type":"number","example":2,"nullable":true},"previousPage":{"type":"number","example":null,"nullable":true}},"required":["total","totalPages","nextPage","previousPage"]},"TagEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Important"},"nameLowercase":{"type":"string","example":"important"},"backgroundColor":{"type":"string","example":"#FDE68A"},"textColor":{"type":"string","example":"#92400E"},"type":{"type":"string","enum":["system","organization","case","user"]},"severity":{"type":"string","enum":["info","warning","danger"]},"origin":{"type":"string","enum":["manual","auto-import"]},"creatorAccessType":{"type":"string","enum":["org","opposing_council"],"nullable":true},"isActive":{"type":"boolean","example":true}},"required":["createdAt","updatedAt","id","name","nameLowercase","backgroundColor","textColor","type","severity","origin","isActive"]},"CreateOrganizationTagDto":{"type":"object","properties":{"name":{"type":"string"},"backgroundColor":{"type":"string"},"textColor":{"type":"string"},"severity":{"type":"string","enum":["info","warning","danger"],"default":"info"}},"required":["name"]},"UpdateOrganizationTagDto":{"type":"object","properties":{"name":{"type":"string"},"backgroundColor":{"type":"string"},"textColor":{"type":"string"},"severity":{"type":"string","enum":["info","warning","danger"],"default":"info"}}},"CaseIntegrationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"integrationType":{"type":"string","enum":["goodfact","clio","smokeball"],"example":"clio"},"attachedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"remoteCaseId":{"type":"string","example":"1234567890"},"remoteCaseName":{"type":"string","example":"Doe v. Example Corp"}},"required":["createdAt","updatedAt","id","integrationType","attachedAt","remoteCaseId","remoteCaseName"]},"AttachedTagResponseEntity":{"type":"object","properties":{"tagId":{"type":"string","description":"Tag id (Tag._id)","example":"507f1f77bcf86cd799439011"},"tag":{"type":"string","description":"Tag display name (Tag.name)","example":"Important"},"addedBy":{"type":"string","enum":["user","ai","auto","auto-import","system","default"]},"addedById":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439012"},"removedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"removedBy":{"type":"string","nullable":true,"enum":["user","ai","auto","auto-import","system","default"],"example":null},"removedById":{"type":"string","nullable":true,"example":null}},"required":["tagId","tag","addedBy"]},"CaseEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Doe v. Example Corp"},"slug":{"type":"string","example":"doe-v-example-corp"},"description":{"type":"string","example":"Employment dispute","nullable":true},"matterId":{"type":"string","nullable":true,"example":"MAT-0042"},"status":{"type":"string","enum":["open","closed","archived","pending"],"example":"open"},"assignStatus":{"type":"string","enum":["assigned","unassigned"],"example":"assigned"},"createdBy":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"assignedTo":{"description":"Assigned user ids","example":["507f1f77bcf86cd799439011"],"type":"array","items":{"type":"string"}},"organization":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/OrganizationEntity"}]},"isActive":{"type":"boolean","example":true},"tags":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/TagEntity"}},"priority":{"type":"string","enum":["low","medium","high","urgent"],"example":"medium"},"attachedDevices":{"description":"Attached device ids","example":["507f1f77bcf86cd799439012"],"type":"array","items":{"type":"string"}},"integrations":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/CaseIntegrationEntity"}},"totalConversations":{"type":"number","example":128},"archivedBy":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"exportJobId":{"type":"string","example":"job-1234","nullable":true},"metadata":{"type":"object","example":{}},"extractionCodePaymentThreadId":{"type":"string","nullable":true,"example":null},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}},"ocCase":{"type":"boolean","example":false},"isOcShared":{"type":"boolean","example":false},"proEnabled":{"type":"boolean","example":false},"isArchived":{"type":"boolean","example":false}},"required":["createdAt","updatedAt","id","name","slug","description","status","assignStatus","createdBy","assignedTo","organization","isActive","tags","priority","attachedDevices","integrations","totalConversations","archivedBy","exportJobId","metadata","extractionCodePaymentThreadId","ocCase","isOcShared","proEnabled","isArchived"]},"UserWithCaseAccessEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"user":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"status":{"type":"string","example":"active"},"level":{"type":"string","example":"admin"},"lastAccessedAt":{"format":"date-time","type":"string","example":"2026-01-16T09:12:00.000Z"}},"required":["createdAt","updatedAt","id","user","status","level","lastAccessedAt"]},"CaseInvitationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"invitedUserEmail":{"type":"string","example":"jane.doe@example.com"},"invitedUserName":{"type":"string","example":"Jane Doe"},"invitedBy":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"organization":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"case":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"invitedUserRole":{"type":"string","example":"member"},"status":{"type":"string","example":"pending"},"emailSent":{"type":"boolean","example":true},"emailSentAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"acceptedAt":{"format":"date-time","type":"string","example":null,"nullable":true},"acceptedBy":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"invitedUser":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"rejectedAt":{"format":"date-time","type":"string","example":null,"nullable":true},"rejectedReason":{"type":"string","example":"Not involved in this matter","nullable":true},"initials":{"type":"string","example":"JD"}},"required":["createdAt","updatedAt","id","invitedUserEmail","invitedUserName","invitedBy","organization","case","invitedUserRole","status","emailSent","emailSentAt","acceptedAt","acceptedBy","invitedUser","rejectedAt","rejectedReason","initials"]},"CaseListItemEntity":{"type":"object","properties":{"case":{"$ref":"#/components/schemas/CaseEntity"},"usersWithAccess":{"type":"array","items":{"$ref":"#/components/schemas/UserWithCaseAccessEntity"}},"usersWithPendingAccess":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/CaseInvitationEntity"}},"conversationsCount":{"type":"number","example":128},"extractionCodes":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeEntity"}},"totalExtractionCodes":{"type":"number","example":5},"dataUsedInBytes":{"type":"number","example":52428800},"dataSourcesStatusCount":{"type":"object","example":{"completed":4,"in_progress":1}}},"required":["case","usersWithAccess","usersWithPendingAccess","conversationsCount","extractionCodes","totalExtractionCodes","dataUsedInBytes","dataSourcesStatusCount"]},"CaseViewEntity":{"type":"object","properties":{"case":{"$ref":"#/components/schemas/CaseEntity"},"usersWithAccess":{"type":"array","items":{"$ref":"#/components/schemas/UserWithCaseAccessEntity"}},"usersWithPendingAccess":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/CaseInvitationEntity"}},"conversationsCount":{"type":"number","example":128},"extractionCodes":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeEntity"}},"totalExtractionCodes":{"type":"number","example":5},"dataSourcesStatusCount":{"type":"object","example":{"completed":4,"in_progress":1}}},"required":["case","usersWithAccess","usersWithPendingAccess","conversationsCount","extractionCodes","totalExtractionCodes","dataSourcesStatusCount"]},"ExtractionCodeConfigEnableLocalExportDefaultConfigDto":{"type":"object","properties":{"chunkType":{"type":"string","enum":["year","month","week","day","none"],"default":"none"},"includeAttachments":{"type":"boolean","default":true}},"required":["chunkType"]},"ExtractionCodeConfigEnableLocalExportDto":{"type":"object","properties":{"formats":{"default":[],"type":"array","items":{"type":"string"}},"defaultConfig":{"default":{"chunkType":"none","includeAttachments":true},"allOf":[{"$ref":"#/components/schemas/ExtractionCodeConfigEnableLocalExportDefaultConfigDto"}]}}},"ExtractionCodeConfigAllowClientDto":{"type":"object","properties":{"allowSocialApps":{"default":[],"type":"array","items":{"type":"string"}},"allowShareAll":{"type":"boolean","default":false}}},"ExtractionCodeConfigDateRangeDto":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]},"ExtractionCodeConfigAutoShareSearchesDto":{"type":"object","properties":{"type":{"type":"string","enum":["name","phone","keywords","email"],"default":"name"},"values":{"type":"array","items":{"type":"string"}},"apps":{"default":[],"type":"array","items":{"type":"string"}},"dateRange":{"default":null,"allOf":[{"$ref":"#/components/schemas/ExtractionCodeConfigDateRangeDto"}]},"exactMatch":{"type":"boolean","default":false},"include":{"type":"boolean","default":true},"conversationType":{"type":"string","enum":["all","dm","group"],"default":"all"}},"required":["type","values","conversationType"]},"ExtractionCodeConfigAutoShareDto":{"type":"object","properties":{"allowClientApproval":{"type":"boolean"},"allowAdditionalShare":{"type":"boolean"},"searches":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeConfigAutoShareSearchesDto"}}}},"ExtractionCodeConfigShareAllSearchesExcludeDto":{"type":"object","properties":{"type":{"type":"string","enum":["name","phone","keywords","email"],"default":"name"},"values":{"type":"array","items":{"type":"string"}},"apps":{"type":"array","items":{"type":"string"}},"exactMatch":{"type":"boolean"},"conversationType":{"type":"string","enum":["all","dm","group"],"default":"all"}},"required":["type","values","conversationType"]},"ExtractionCodeConfigShareAllDto":{"type":"object","properties":{"apps":{"type":"array","items":{"type":"string"}},"includeAttachments":{"type":"boolean","default":true},"dateRange":{"$ref":"#/components/schemas/ExtractionCodeConfigDateRangeDto"},"searchesExclude":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeConfigShareAllSearchesExcludeDto"}}}},"ExtractionCodeConfigAppConfigConcurrencyDto":{"type":"object","properties":{"mediaType":{"type":"string","enum":["images","video","audio","documents"]},"concurrency":{"type":"array","items":{"type":"number"}}},"required":["mediaType"]},"ExtractionCodeConfigAppConfigDto":{"type":"object","properties":{"conversionConcurrency":{"$ref":"#/components/schemas/ExtractionCodeConfigAppConfigConcurrencyDto"},"compressionConcurrency":{"$ref":"#/components/schemas/ExtractionCodeConfigAppConfigConcurrencyDto"},"enableLocalMediaConversion":{"type":"boolean","default":false},"enableLocalMediaCompression":{"type":"boolean","default":false},"enableIosEncryptedBackupExtraction":{"type":"boolean","default":false},"allowOnlyEncryptedIosBackup":{"type":"boolean","default":false},"autoIncludeSafariData":{"type":"boolean","default":false},"autoIncludeNotesData":{"type":"boolean","default":false},"autoIncludeContacts":{"type":"boolean","default":false},"shareAction":{"type":"string","enum":["share","export","share_and_export"],"default":"share"}}},"ExtractionCodeConfigDto":{"type":"object","properties":{"type":{"type":"string","enum":["allow_client","share_all","auto_share"],"default":"allow_client"},"ignoreMediaPermissionAndroid":{"type":"boolean","default":false},"origin":{"type":"string"},"enableFileUpload":{"type":"boolean","default":false},"enableLocalExport":{"$ref":"#/components/schemas/ExtractionCodeConfigEnableLocalExportDto"},"allowClient":{"$ref":"#/components/schemas/ExtractionCodeConfigAllowClientDto"},"autoShare":{"$ref":"#/components/schemas/ExtractionCodeConfigAutoShareDto"},"shareAll":{"$ref":"#/components/schemas/ExtractionCodeConfigShareAllDto"},"dateRange":{"$ref":"#/components/schemas/ExtractionCodeConfigDateRangeDto"},"appConfig":{"$ref":"#/components/schemas/ExtractionCodeConfigAppConfigDto"}},"required":["type"]},"ExtractionCodeSendRemindersDto":{"type":"object","properties":{"dueIn2Weeks":{"type":"boolean","default":false},"dueIn1Week":{"type":"boolean","default":false},"dueIn3Days":{"type":"boolean","default":false},"dueIn1Day":{"type":"boolean","default":false},"after1Week":{"type":"boolean","default":false},"after2Weeks":{"type":"boolean","default":false},"after1Month":{"type":"boolean","default":false}}},"PrepaidItemDto":{"type":"object","properties":{"productKey":{"type":"string","enum":["prepaid.device","prepaid.email","prepaid.device-email","prepaid.support"]},"quantity":{"type":"number","default":1},"unitAmountCents":{"type":"number","description":"Negotiated unit price in cents"}},"required":["productKey","quantity","unitAmountCents"]},"InviteCaseUser":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string","minLength":1,"maxLength":200},"accessLevel":{"type":"string","enum":["owner","admin","commenter","viewer"],"default":"viewer"},"sendInstructions":{"type":"boolean","default":false},"accessType":{"type":"string","enum":["org","opposing_council"],"default":"org"},"orgName":{"type":"string","maxLength":200}},"required":["email","accessLevel"]},"CreateCaseDto":{"type":"object","properties":{"custodianEmail":{"type":"string","maxLength":100,"minLength":3},"custodianName":{"type":"string","maxLength":100,"minLength":3},"custodianPhone":{"type":"string","maxLength":50,"minLength":3},"sendEmail":{"type":"boolean"},"label":{"type":"string","maxLength":100,"minLength":3},"allowShareAll":{"type":"boolean","deprecated":true,"description":"Use config instead"},"config":{"$ref":"#/components/schemas/ExtractionCodeConfigDto"},"requestedSources":{"type":"array","items":{"type":"string"}},"dueDate":{"type":"string"},"sendReminders":{"$ref":"#/components/schemas/ExtractionCodeSendRemindersDto"},"metadata":{"type":"object"},"watchlist":{"type":"boolean","default":false},"ocRequired":{"type":"boolean","default":true,"description":"OC cases only: when false, the OL creates/keeps this code without the opposing-council approval gate (code stays `na`, immediately usable, and the custodian email is sent). Ignored on non-OC cases. Defaults to true (gate on)."},"paymentRequired":{"type":"boolean","description":"Whether payment is required for this extraction code"},"paymentAmount":{"type":"number","description":"Payment amount in cents"},"paymentCurrency":{"type":"string","description":"Payment currency (defaults to usd)","default":"usd"},"paymentSuccessRedirectUrl":{"type":"string","description":"Payment success redirect URL"},"productName":{"type":"string","description":"Product name"},"prepaidItems":{"description":"Prepaid basket. Each entry becomes its own Stripe line item and grants units on payment. Prices are negotiable — unitAmountCents is what the customer is charged, and is never validated against the catalog default.","type":"array","items":{"$ref":"#/components/schemas/PrepaidItemDto"}},"name":{"type":"string","maxLength":100,"minLength":3},"description":{"type":"string"},"matterId":{"type":"string","maxLength":100,"description":"The firm's own client/matter reference. Printed on Stripe invoice line items and carried in invoice metadata."},"tags":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["open","closed","archived","pending"],"default":"pending"},"priority":{"type":"string","enum":["low","medium","high","urgent"],"default":"low"},"isDefault":{"type":"boolean","default":false},"createExtractionCode":{"type":"boolean","default":false},"addExistingUsersToCase":{"type":"boolean","default":false},"addExistingUsersAccessLevel":{"type":"string","enum":["owner","admin","commenter","viewer"],"default":"admin"},"users":{"type":"array","items":{"$ref":"#/components/schemas/InviteCaseUser"}}},"required":["dueDate","name"]},"CheckCaseNameResponseEntity":{"type":"object","properties":{"isAvailable":{"type":"boolean","example":true}},"required":["isAvailable"]},"CaseDataSourceListItemEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"string","example":"507f1f77bcf86cd799439011"},"dataSource":{"type":"string","description":"Id of the master data source","example":"507f1f77bcf86cd799439012"}},"required":["createdAt","updatedAt","id","case","dataSource"]},"UpdateCaseDto":{"type":"object","properties":{"name":{"type":"string","maxLength":100,"minLength":3},"description":{"type":"string"},"matterId":{"type":"string","maxLength":100,"description":"The firm's own client/matter reference. Printed on Stripe invoice line items and carried in invoice metadata."},"tags":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["open","closed","archived","pending"],"default":"pending"},"priority":{"type":"string","enum":["low","medium","high","urgent"],"default":"low"},"metadata":{"type":"object"}}},"ArchiveCaseDto":{"type":"object","properties":{"deleteImmediately":{"type":"boolean","description":"Schedule to delete immediately after archiving","example":false,"default":false}},"required":["deleteImmediately"]},"LinkConversationsToCaseDto":{"type":"object","properties":{"conversations":{"type":"array","items":{"type":"array"}}},"required":["conversations"]},"UnlinkConversationsFromCaseDto":{"type":"object","properties":{"conversations":{"type":"array","items":{"type":"array"}}},"required":["conversations"]},"CaseCollectionExportEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"organization":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"device":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"type":{"type":"string","enum":["doc","text","csv","pdf","goodfact","rsmf","eml","files-zip"]},"source":{"type":"string","enum":["conversation","browser-history","emails","case-files","case-attachments","financial","linkedin","ai-chat","contacts"]},"chunkType":{"type":"string","enum":["none","day","week","month","year"]},"exportedBy":{"type":"string","nullable":true,"description":"Id of the user who requested the export","example":"507f1f77bcf86cd799439011"},"tags":{"type":"array","items":{"type":"object"},"nullable":true,"example":[],"description":"Not loaded by any route, so always null or an empty list."},"emails":{"example":["jane.doe@example.com"],"type":"array","items":{"type":"string"}},"caseFileIds":{"description":"Snapshot of CaseFile ids resolved at request time (only set when source = case-files)","example":["507f1f77bcf86cd799439015"],"type":"array","items":{"type":"string"}},"fileName":{"type":"string","example":"doe-v-example-export.pdf","nullable":true},"file":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileEntity"}]},"sortBy":{"type":"string","enum":["old-to-new","new-to-old"]},"size":{"type":"number","description":"Size in bytes","example":1048576},"errorMessage":{"type":"string","example":null,"nullable":true},"totalMessages":{"type":"number","example":1200},"messagesProcessed":{"type":"number","example":600},"totalConversations":{"type":"number","example":12},"conversationsProcessed":{"type":"number","example":6},"status":{"type":"string","enum":["not-started","inprogress","completed","failed","cancelled"]},"jobId":{"type":"string","example":"job-1234","nullable":true},"totalJobs":{"type":"number","example":1,"description":"Number of chunk jobs this export was split into"},"emailData":{"example":[],"items":{"type":"array","items":{"type":"object"}},"type":"array"},"metadata":{"type":"object","example":null,"nullable":true},"failedThreadsCount":{"type":"number","example":0,"description":"Email threads this export could not render"},"cmpSyncStatus":{"type":"string","enum":["pending","processing","completed","failed","disabled","skipped"],"nullable":true,"description":"CMP delivery status when this export was sent to a provider (pending/processing/completed/failed/…). Null when the export was not sent to a CMP — i.e. a plain download-to-computer export.","example":null},"progress":{"type":"number","example":100},"conversationNames":{"example":["Jane Doe"],"type":"array","items":{"type":"string"}},"caseName":{"type":"string","example":"Doe v. Example Corp"}},"required":["createdAt","updatedAt","id","organization","device","type","source","chunkType","exportedBy","tags","emails","fileName","file","sortBy","size","errorMessage","totalMessages","messagesProcessed","totalConversations","conversationsProcessed","status","jobId","totalJobs","emailData","progress","conversationNames","caseName"]},"EmailExportEmailSearchDto":{"type":"object","properties":{"searchText":{"type":"string","description":"Text search applied to all indexed fields (subject, body, snippet, from/to/cc/bcc). Supports the same AND / OR / NOT / -prefix, parentheses, \"quoted phrases\" and wildcards as the boolean query parser.","example":"(discriminate OR racist) AND (\"Larry Graham\" OR Robinson)"},"folders":{"type":"array","items":{"type":"string"}},"domains":{"description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive. Requires the `domains` field on the emailtext Atlas index.","type":"array","items":{"type":"string"}},"excludeDomains":{"description":"Exclude items whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"excludeTags":{"description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","type":"array","items":{"type":"string"}},"untagged":{"type":"boolean","description":"When truthy (1/true), return only emails with no tags attached. Takes precedence over the `tags` filter."},"participants":{"description":"Filter by participant email address(es), any-of.","type":"array","items":{"type":"string"}},"excludeEmails":{"description":"Exclude items whose participant address matches any of these (any-of). Must not overlap `participants`.","type":"array","items":{"type":"string"}},"startDate":{"type":"string","format":"date-time","description":"Only return emails whose `date` is at/after this instant (inclusive). Accepts an ISO-8601 date-time.","example":"2024-01-01T00:00:00.000Z"},"endDate":{"type":"string","format":"date-time","description":"Only return emails whose `date` is at/before this instant (inclusive). Accepts an ISO-8601 date-time.","example":"2024-12-31T23:59:59.999Z"},"ids":{"description":"Restrict to these email ids, AND-intersected with the other filters.","type":"array","items":{"type":"string"}},"exportWholeThread":{"type":"boolean","default":false,"description":"When true, export the full threads containing the matched emails. When false (default), export only the matched emails, grouped under their thread."}}},"EmailExportThreadSearchDto":{"type":"object","properties":{"folders":{"type":"array","items":{"type":"string"}},"domains":{"description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive.","type":"array","items":{"type":"string"}},"excludeDomains":{"description":"Exclude items whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"excludeTags":{"description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","type":"array","items":{"type":"string"}},"participants":{"description":"Filter by participant email address(es), any-of.","type":"array","items":{"type":"string"}},"excludeEmails":{"description":"Exclude items whose participant address matches any of these (any-of). Must not overlap `participants`.","type":"array","items":{"type":"string"}},"starred":{"type":"boolean"},"unread":{"type":"boolean"},"untagged":{"type":"boolean","description":"When truthy (1/true), return only threads with no tags attached. Takes precedence over the `tags` filter."},"startDate":{"type":"string","format":"date-time","description":"Only return threads whose `latestMessageReceivedDate` is at/after this instant (inclusive). Accepts an ISO-8601 date-time.","example":"2024-01-01T00:00:00.000Z"},"endDate":{"type":"string","format":"date-time","description":"Only return threads whose `latestMessageReceivedDate` is at/before this instant (inclusive). Accepts an ISO-8601 date-time.","example":"2024-12-31T23:59:59.999Z"},"ids":{"description":"Restrict to these thread ids, AND-intersected with the other filters.","type":"array","items":{"type":"string"}}}},"EmailExportConfigDto":{"type":"object","properties":{"providerId":{"type":"string","description":"Email data provider to export from"},"emailSearch":{"$ref":"#/components/schemas/EmailExportEmailSearchDto"},"threadSearch":{"$ref":"#/components/schemas/EmailExportThreadSearchDto"},"includeAttachments":{"type":"boolean","default":false,"description":"When true, each email's attachments are bundled into the export alongside the rendered PDF/EML output. Attachments are streamed to S3 and are subject to the files-zip size limits."}},"required":["providerId"]},"CaseCollectionExportDto":{"type":"object","properties":{"collection":{"type":"string","deprecated":true},"conversations":{"type":"array","items":{"type":"array"}},"excludeConversations":{"type":"array","items":{"type":"array"}},"excludeDeviceSources":{"type":"array","description":"Device conversation sources to exclude from the export. Applies to device-conversation exports only.","items":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"]}},"type":{"type":"string","enum":["doc","text","csv","pdf","goodfact","rsmf","eml","files-zip"]},"source":{"type":"string","enum":["conversation","browser-history","emails","case-files","case-attachments","financial","linkedin","ai-chat","contacts"]},"chunkType":{"type":"string","enum":["none","day","week","month","year"],"default":"none"},"emails":{"description":"Additional email addresses to send email to when export completes (other than user email)","type":"array","items":{"type":"string"}},"fileName":{"type":"string"},"exportConfig":{"type":"array","items":{"type":"string","enum":["phone-numbers","comments","tags","app-icons","device-name","images","hide-attachment-links","extra-texts","line-break","device-logs","custom-entry","recently-deleted","deleted-messages-inline","deleted-messages-page","deleted-messages-inline-page","ignore-conversations","search-history-page","hide-page-numbers","print-footer-work-timestamp"]}},"sortBy":{"type":"string","enum":["old-to-new","new-to-old"]},"startDate":{"type":"string"},"endDate":{"type":"string"},"tags":{"type":"array","items":{"type":"array"}},"tagsPadding":{"type":"number"},"device":{"type":"string"},"emailExportConfig":{"$ref":"#/components/schemas/EmailExportConfigDto"},"includeHiddenEmailThreads":{"type":"boolean","default":false,"description":"When true, hidden threads (hiddenAt != null) are included in the email export. Default false. Does not affect explicitly-provided emailDataThreads ids, which are always exported."},"timezone":{"type":"string","description":"IANA timezone string (e.g. \"America/New_York\") for formatting the export timestamp"}},"required":["type","sortBy"]},"DeviceEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"deviceId":{"type":"string","example":"3F2504E0-4F89-11D3-9A0C-0305E82C3301"},"serialNumber":{"type":"string","example":"F2LXK0QJHG7F"},"name":{"type":"string","example":"Jane's iPhone"},"type":{"type":"string","example":"ios"},"nonDeviceType":{"type":"string","nullable":true,"example":null},"model":{"type":"string","example":"iPhone 15"},"timeZone":{"type":"string","example":"America/New_York"},"osVersion":{"type":"string","example":"18.0"},"owner":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"devicePhoneNumber":{"type":"string","example":"+15555550100"},"organizations":{"description":"Organization ids","example":["507f1f77bcf86cd799439011"],"type":"array","items":{"type":"string"}},"extractionCodes":{"description":"Extraction code ids","example":["507f1f77bcf86cd799439013"],"type":"array","items":{"type":"string"}},"availableData":{"type":"object","description":"Which data types exist for this device (optionally scoped by query filters).","example":{"messages":true,"calls":false}}},"required":["createdAt","updatedAt","id","deviceId","serialNumber","name","type","nonDeviceType","model","timeZone","osVersion","owner","devicePhoneNumber","organizations","extractionCodes"]},"UserEmailDataProviderEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"email":{"type":"string","example":"jane.doe@example.com"},"ecCode":{"type":"string","description":"Never sent today: no importer fills it.","example":"K7Q2-9XPL"},"grantId":{"type":"string","example":"5f0c8b3e-2a41-4d7c-9a51-1c2b3d4e5f60","nullable":true},"provider":{"type":"string","example":"google","nullable":true},"isValid":{"type":"boolean","example":true},"lastAccessedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"folders":{"type":"array","description":"Mailbox folders saved when the account was connected or its folders were refreshed.","items":{"type":"object","properties":{"name":{"type":"string","example":"INBOX"},"id":{"type":"string","example":"INBOX"},"isSystemFolder":{"type":"boolean","example":true},"backgroundColor":{"type":"string","example":"#ffffff"},"metadata":{"type":"object","description":"A copy of the provider's folder record. OAuth (Nylas) accounts: id, name, object, grantId, and depending on the provider backgroundColor, textColor, systemFolder, parentId, childCount, totalCount, unreadCount, attributes. IMAP accounts: name, id, isSystemFolder, backgroundColor and an empty metadata object.","additionalProperties":true,"example":{"grantId":"5f0c8b3e-2a41-4d7c-9a51-1c2b3d4e5f60","id":"INBOX","name":"INBOX","object":"folder","systemFolder":true}}}},"example":[{"backgroundColor":"#ffffff","id":"INBOX","isSystemFolder":true,"metadata":{"grantId":"5f0c8b3e-2a41-4d7c-9a51-1c2b3d4e5f60","id":"INBOX","name":"INBOX","object":"folder","systemFolder":true},"name":"INBOX"}]}},"required":["createdAt","updatedAt","id","email","grantId","provider","isValid","lastAccessedAt","folders"]},"CaseFileProviderEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"extractionCode":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."}},"required":["createdAt","updatedAt","id","case","extractionCode"]},"DiscordUserProviderEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"discordUserId":{"type":"string","example":"1012345678901234567"},"username":{"type":"string","example":"janedoe","nullable":true},"globalName":{"type":"string","example":"Jane Doe","nullable":true},"discriminator":{"type":"number","example":0,"nullable":true},"email":{"type":"string","example":"jane.doe@example.com","nullable":true},"phone":{"type":"string","example":"+15555550100","nullable":true},"avatar":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"verified":{"type":"boolean","example":true},"flags":{"example":[],"type":"array","items":{"type":"string"}},"servers":{"type":"array","items":{"type":"object"},"example":[{"name":"Example Server","serverId":"1023456789012345678"}]},"adsProfile":{"type":"object","example":{"age":"25-34","gender":"female"},"nullable":true},"accountData":{"type":"object","example":{"discord_store":{"orders":[]}},"nullable":true},"settings":{"type":"object","example":{"locale":"en-US","theme":"dark"},"nullable":true},"metadata":{"type":"object","example":{"avatar_hash":null,"has_mobile":true,"premium_until":null},"nullable":true},"deletedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"deletedBy":{"type":"object","nullable":true,"example":null},"ocSharedStatus":{"type":"string","nullable":true,"enum":["shared","pending","not_gated"],"description":"Opposing-council only: whether this provider has been shared with the OL. `null` for OL/OG callers — same convention as every other item listing. Provider-level: every item under it shares this status, since approval here is all-or-nothing."}},"required":["createdAt","updatedAt","id","discordUserId","username","globalName","discriminator","email","phone","avatar","verified","flags","servers","adsProfile","accountData","settings","metadata","deletedAt","deletedBy"]},"RedditUserProviderEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"redditUserId":{"type":"string","example":"jane_doe_42"},"email":{"type":"string","description":"Not filled by any importer today, so always null.","example":null,"nullable":true},"emailVerified":{"type":"boolean","description":"Not filled by any importer today, so always false.","example":false},"registrationDate":{"format":"date-time","type":"string","description":"Not filled by any importer today, so always null.","example":null,"nullable":true},"isDeleted":{"type":"boolean","example":false},"accountGender":{"type":"string","example":"female","nullable":true},"birthdate":{"type":"string","example":"1990-04-12","nullable":true},"linkedIdentities":{"type":"array","items":{"type":"object"},"example":[{"issuerId":"https://accounts.google.com","subjectId":"104857600123456789012"}]},"friends":{"type":"array","items":{"type":"object"},"example":[{"note":"","username":"john_smith_7"}]},"subscribedSubreddits":{"example":["AskReddit","personalfinance"],"type":"array","items":{"type":"string"}},"gildedContent":{"type":"array","items":{"type":"object"},"example":[{"amount":1,"award":"gold","contentLink":"https://www.reddit.com/r/AskReddit/comments/1abc2de/comment/kx1y2z3/","date":"2026-01-15T14:30:00.000Z"}]},"multireddits":{"type":"array","items":{"type":"object"},"example":[{"display_name":"news","id":"m4abc1","subreddits":"worldnews,news"}]},"ipLogs":{"type":"array","items":{"type":"object"},"example":[{"date":"2026-01-15 14:30:00 UTC","ip":"203.0.113.7"}]},"deletedAt":{"format":"date-time","type":"string","example":null,"nullable":true},"deletedBy":{"type":"string","nullable":true,"example":null},"ocSharedStatus":{"type":"string","nullable":true,"enum":["shared","pending","not_gated"],"description":"Opposing-council only: whether this provider has been shared with the OL. `null` for OL/OG callers — same convention as every other item listing. Provider-level: every item under it shares this status, since approval here is all-or-nothing."}},"required":["createdAt","updatedAt","id","redditUserId","email","emailVerified","registrationDate","isDeleted","accountGender","birthdate","linkedIdentities","friends","subscribedSubreddits","gildedContent","multireddits","ipLogs","deletedAt","deletedBy"]},"AiChatProviderEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"platform":{"type":"string","enum":["claude","chatgpt","gemini"],"example":"chatgpt"},"externalUserId":{"type":"string","example":"user-a1B2c3D4e5F6g7H8i9J0k1L2"},"email":{"type":"string","example":"jane.doe@example.com","nullable":true},"displayName":{"type":"string","example":"Jane Doe","nullable":true},"accountPlan":{"type":"string","example":"plus","nullable":true},"accountData":{"type":"object","example":{"id":"user-a1B2c3D4e5F6g7H8i9J0k1L2","email":"jane.doe@example.com","name":"Jane Doe","chatgpt_plus_user":true},"nullable":true},"metadata":{"type":"object","description":"Not filled by any importer today, so always null.","example":null,"nullable":true},"deletedAt":{"format":"date-time","type":"string","example":null,"nullable":true},"deletedBy":{"type":"string","nullable":true,"example":null},"ocSharedStatus":{"type":"string","nullable":true,"enum":["shared","pending","not_gated"],"description":"Opposing-council only: whether this provider has been shared with the OL. `null` for OL/OG callers — same convention as every other item listing. Provider-level: every item under it shares this status, since approval here is all-or-nothing."}},"required":["createdAt","updatedAt","id","platform","externalUserId","email","displayName","accountPlan","accountData","metadata","deletedAt","deletedBy"]},"CaseDataStatusRowEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"dataSource":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"caseDataSource":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"extractionCode":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"status":{"type":"string","enum":["pending","in_progress","staged","completed","failed"]},"sourceType":{"type":"string","enum":["Device","UserEmailDataProvider","CaseFileProvider","FinancialAccountProvider","LegalDocumentProvider","DiscordUserProvider","RedditUserProvider","LinkedInUserProvider","AiChatProvider"]},"itemsCount":{"type":"number","example":1250},"pendingOcApprovalCount":{"type":"number","description":"Set only for an Originating-org Lawyer on an OC-enabled case: count of items uploaded but not yet OC-approved for this source. Omitted for OC callers and for OL callers on non-OC cases.","example":0},"device":{"$ref":"#/components/schemas/DeviceEntity"},"emailProvider":{"$ref":"#/components/schemas/UserEmailDataProviderEntity"},"fileProvider":{"$ref":"#/components/schemas/CaseFileProviderEntity"},"discordProvider":{"$ref":"#/components/schemas/DiscordUserProviderEntity"},"redditProvider":{"$ref":"#/components/schemas/RedditUserProviderEntity"},"aiChatProvider":{"$ref":"#/components/schemas/AiChatProviderEntity"}},"required":["createdAt","updatedAt","id","case","dataSource","caseDataSource","extractionCode","status","sourceType","itemsCount"]},"CaseDataStatusResponseSingleEntity":{"type":"object","properties":{"dataSource":{"$ref":"#/components/schemas/MasterDataSourceEntity"},"percentages":{"example":[{"status":"completed","percentage":66.67},{"status":"in_progress","percentage":33.33}],"type":"array","items":{"type":"object"}},"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseDataStatusRowEntity"}}},"required":["dataSource","percentages","data"]},"CaseDataStatusResponseEntity":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseDataStatusResponseSingleEntity"}}},"required":["data"]},"CaseDataStatusCustodianEntity":{"type":"object","properties":{"name":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane.doe@example.com"}},"required":["name","email"]},"CaseDataStatusDataSourceResponseEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"type":{"type":"string","example":"iMessage","description":"Name of the master data source"},"category":{"type":"string","example":null,"nullable":true},"name":{"type":"string","example":"iPhone 15"},"status":{"type":"string","example":"completed"},"itemsCount":{"type":"number","example":1250},"pendingOcApprovalCount":{"type":"number","description":"Set only for an Originating-org Lawyer on an OC-enabled case: count of items uploaded but not yet OC-approved for this source. Omitted for OC callers and for OL callers on non-OC cases.","example":0},"ocSharedStatus":{"type":"string","enum":["shared","pending","not_gated"],"description":"Opposing-council only: how this source has been shared with the OL. `shared`/`pending` summarize a partially-approved source by whether anything is still outstanding — see pendingOcApprovalCount for the exact split. Omitted for OL/OG callers."},"sizeBytes":{"type":"number","example":52428800},"dateCompleted":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"sourceId":{"type":"string","example":"507f1f77bcf86cd799439012"}},"required":["id","type","category","name","status"]},"CaseDataStatusResponseSingleV2Entity":{"type":"object","properties":{"code":{"type":"string","example":"K7Q2-9XPL"},"createdDate":{"format":"date-time","type":"string","example":"2026-01-10T09:00:00.000Z"},"dueDate":{"format":"date-time","type":"string","example":"2026-02-01T00:00:00.000Z"},"dateRangeStart":{"format":"date-time","type":"string","example":"2025-01-01T00:00:00.000Z"},"dateRangeEnd":{"format":"date-time","type":"string","example":"2025-12-31T23:59:59.000Z"},"collectionMethod":{"type":"string","example":"allow_client"},"notes":{"type":"string","example":null,"nullable":true},"custodian":{"$ref":"#/components/schemas/CaseDataStatusCustodianEntity"},"dataSources":{"type":"array","items":{"$ref":"#/components/schemas/CaseDataStatusDataSourceResponseEntity"}}},"required":["code","createdDate","collectionMethod","dataSources"]},"CaseDataStatusV2ResponseEntity":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseDataStatusResponseSingleV2Entity"}}},"required":["data"]},"CaseDataStatusListTreeNodesResponseEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"kind":{"type":"string","example":"financial"},"sourceId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"sourceName":{"type":"string","nullable":true,"example":"jane.doe@example.com"},"plaidItemId":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e2"},"institutionName":{"type":"string","nullable":true,"example":"Example Bank"},"itemsCount":{"type":"number","example":3},"pendingOcApprovalCount":{"type":"number","description":"Set only for an Originating-org Lawyer on an OC-enabled case: count of items uploaded but not yet OC-approved for this source node. Omitted for OC callers and for OL callers on non-OC cases.","example":0},"backupStatus":{"type":"string","enum":["processing","completed","failed","na"],"nullable":true},"children":{"type":"array","items":{"type":"object"},"example":[{"name":"Messages","count":120,"subCount":3}]}},"required":["id","kind"]},"CaseDataStatusListTreeResponseEntity":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseDataStatusListTreeNodesResponseEntity"}}},"required":["data"]},"V3TreeSummaryChildEntity":{"type":"object","properties":{"name":{"type":"string","example":"Messages"},"count":{"type":"number","example":320},"subCount":{"type":"number","example":12}},"required":["name","count","subCount"]},"V3ConversationChildEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439014"},"name":{"type":"string","example":"Jane Doe"},"source":{"type":"string","example":"imessage"},"messagesCount":{"type":"number","example":320},"lastMessageDate":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T14:30:00.000Z"},"isPinned":{"type":"boolean","example":false}},"required":["id","name","source","messagesCount","lastMessageDate","isPinned"]},"V3FinancialChildEntity":{"type":"object","properties":{"name":{"type":"string","example":"Everyday Checking"},"accountId":{"type":"string","example":"acc_8Xq2"},"financialNumber":{"type":"string","nullable":true,"example":"****1234"},"financialInsights":{"type":"number","example":12},"accountType":{"type":"string","nullable":true,"example":"depository"},"plaidItemId":{"type":"string","example":"item_4Lm9"},"financialAccountId":{"type":"string","example":"507f1f77bcf86cd799439013"}},"required":["name","accountId","financialNumber","financialInsights","accountType","plaidItemId","financialAccountId"]},"V3TreeNodeEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"kind":{"type":"string","example":"device"},"sourceId":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439012"},"deviceName":{"type":"string","nullable":true,"example":"iPhone 15"},"sourceName":{"type":"string","nullable":true,"example":"iMessage"},"plaidItemId":{"type":"string","nullable":true,"example":null},"institutionName":{"type":"string","nullable":true,"example":null},"itemsCount":{"type":"number","example":1250},"pendingOcApprovalCount":{"type":"number","description":"Set only for an Originating-org Lawyer on an OC-enabled case: count of items uploaded but not yet OC-approved for this source node. Omitted for OC callers and for OL callers on non-OC cases.","example":0},"children":{"type":"array","items":{"$ref":"#/components/schemas/V3TreeSummaryChildEntity"}},"conversationChildren":{"type":"array","items":{"$ref":"#/components/schemas/V3ConversationChildEntity"}},"conversationTotal":{"type":"number","example":42},"financialChildren":{"type":"array","items":{"$ref":"#/components/schemas/V3FinancialChildEntity"}},"platform":{"type":"string","nullable":true,"example":"ios"}},"required":["id","kind","sourceId","deviceName","sourceName","itemsCount","children"]},"CaseDataStatusTreeV3ResponseEntity":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V3TreeNodeEntity"}}},"required":["data"]},"CreateExtractionCodeDto":{"type":"object","properties":{"custodianEmail":{"type":"string","maxLength":100,"minLength":3},"custodianName":{"type":"string","maxLength":100,"minLength":3},"custodianPhone":{"type":"string","maxLength":50,"minLength":3},"sendEmail":{"type":"boolean"},"label":{"type":"string","maxLength":100,"minLength":3},"allowShareAll":{"type":"boolean","deprecated":true,"description":"Use config instead"},"config":{"$ref":"#/components/schemas/ExtractionCodeConfigDto"},"requestedSources":{"type":"array","items":{"type":"string"}},"dueDate":{"type":"string"},"sendReminders":{"$ref":"#/components/schemas/ExtractionCodeSendRemindersDto"},"metadata":{"type":"object"},"watchlist":{"type":"boolean","default":false},"ocRequired":{"type":"boolean","default":true,"description":"OC cases only: when false, the OL creates/keeps this code without the opposing-council approval gate (code stays `na`, immediately usable, and the custodian email is sent). Ignored on non-OC cases. Defaults to true (gate on)."},"paymentRequired":{"type":"boolean","description":"Whether payment is required for this extraction code"},"paymentAmount":{"type":"number","description":"Payment amount in cents"},"paymentCurrency":{"type":"string","description":"Payment currency (defaults to usd)","default":"usd"},"paymentSuccessRedirectUrl":{"type":"string","description":"Payment success redirect URL"},"productName":{"type":"string","description":"Product name"},"prepaidItems":{"description":"Prepaid basket. Each entry becomes its own Stripe line item and grants units on payment. Prices are negotiable — unitAmountCents is what the customer is charged, and is never validated against the catalog default.","type":"array","items":{"$ref":"#/components/schemas/PrepaidItemDto"}}},"required":["dueDate"]},"UpdateExtractionCodeDto":{"type":"object","properties":{"custodianEmail":{"type":"string","maxLength":100,"minLength":3},"custodianName":{"type":"string","maxLength":100,"minLength":3},"custodianPhone":{"type":"string","maxLength":50,"minLength":3},"sendEmail":{"type":"boolean"},"label":{"type":"string","maxLength":100,"minLength":3},"allowShareAll":{"type":"boolean","deprecated":true,"description":"Use config instead"},"config":{"$ref":"#/components/schemas/ExtractionCodeConfigDto"},"requestedSources":{"type":"array","items":{"type":"string"}},"dueDate":{"type":"string"},"sendReminders":{"$ref":"#/components/schemas/ExtractionCodeSendRemindersDto"},"metadata":{"type":"object"},"watchlist":{"type":"boolean","default":false},"ocRequired":{"type":"boolean","default":true,"description":"OC cases only: when false, the OL creates/keeps this code without the opposing-council approval gate (code stays `na`, immediately usable, and the custodian email is sent). Ignored on non-OC cases. Defaults to true (gate on)."},"paymentRequired":{"type":"boolean","description":"Whether payment is required for this extraction code"},"paymentAmount":{"type":"number","description":"Payment amount in cents"},"paymentCurrency":{"type":"string","description":"Payment currency (defaults to usd)","default":"usd"},"paymentSuccessRedirectUrl":{"type":"string","description":"Payment success redirect URL"},"productName":{"type":"string","description":"Product name"},"prepaidItems":{"description":"Prepaid basket. Each entry becomes its own Stripe line item and grants units on payment. Prices are negotiable — unitAmountCents is what the customer is charged, and is never validated against the catalog default.","type":"array","items":{"$ref":"#/components/schemas/PrepaidItemDto"}},"lockCode":{"type":"boolean","description":"Indicates if the extraction code is locked"}},"required":["dueDate"]},"UpdateCustodianContactDto":{"type":"object","properties":{"custodianEmail":{"type":"string","maxLength":100,"minLength":3},"custodianName":{"type":"string","maxLength":100,"minLength":3},"custodianPhone":{"type":"string","maxLength":50,"minLength":3},"sendEmail":{"type":"boolean","default":true,"description":"Whether to re-send the extraction-code email after updating contact (default true). Even when true, the email only goes out if the code is usable (na/approved) and has a custodian email."}}},"SendExtractionCodeEmailDto":{"type":"object","properties":{"custodianEmail":{"type":"string","maxLength":100,"minLength":3},"custodianName":{"type":"string","maxLength":100,"minLength":3}}},"DeviceSearchTrackingEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"extractionCode":{"type":"string","example":"507f1f77bcf86cd799439011"},"organization":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"string","example":"507f1f77bcf86cd799439011"},"device":{"type":"string","example":"507f1f77bcf86cd799439011","nullable":true},"linkedConversations":{"example":["507f1f77bcf86cd799439011"],"type":"array","items":{"type":"string"}},"searchData":{"type":"array","items":{"type":"object"},"example":[{"action":"search","value":"Jane Doe"},{"action":"select_all","value":true}]}},"required":["createdAt","updatedAt","id","extractionCode","organization","case","device","linkedConversations","searchData"]},"DeviceActivityLogSingleEntity":{"type":"object","properties":{"uploadAttemptId":{"type":"string","example":null,"nullable":true},"selectedMessageUpload":{"type":"object","example":null,"nullable":true},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"extractionCode":{"type":"string","example":"K7Q2-9XPL"},"userName":{"type":"string","example":"Jane Doe"},"userEmail":{"type":"string","example":"jane.doe@example.com"},"type":{"type":"string","example":"backup-completed"},"log":{"type":"string","example":"Backup completed"}},"required":["date","extractionCode","userName","userEmail","type","log"]},"DownloadConversationAttachmentsDto":{"type":"object","properties":{"conversationIds":{"type":"array","items":{"type":"array"}}},"required":["conversationIds"]},"CaseInvitationListItemEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"invitedUserEmail":{"type":"string","example":"jane.doe@example.com"},"invitedUserName":{"type":"string","example":"Jane Doe"},"invitedBy":{"nullable":true,"description":"Who sent the invite","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"invitedUser":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"acceptedBy":{"type":"string","nullable":true,"example":null},"organization":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"string","example":"507f1f77bcf86cd799439011"},"accessOrganization":{"type":"string","nullable":true,"example":null,"description":"The opposing counsel's organization, for OC invites"},"invitedUserRole":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"],"example":"member"},"invitedUserAccessLevel":{"type":"string","enum":["owner","admin","commenter","viewer"]},"status":{"type":"string","enum":["pending","accepted","rejected"],"example":"pending"},"emailSent":{"type":"boolean","example":true},"emailSentAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T14:30:00.000Z"}},"required":["createdAt","updatedAt","id","invitedUserEmail","invitedUserName","invitedBy","invitedUser","acceptedBy","organization","case","accessOrganization","invitedUserRole","invitedUserAccessLevel","status","emailSent","emailSentAt"]},"CaseMemberEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"user":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"level":{"type":"string","enum":["owner","admin","commenter","viewer"]},"accessType":{"type":"string","enum":["org","opposing_council"]},"invitedBy":{"type":"string","nullable":true,"description":"Id of the user who added this member","example":"507f1f77bcf86cd799439011"},"accessGrantedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"status":{"type":"string","enum":["active","inactive","revoked"]},"accessOrganization":{"type":"string","nullable":true,"example":null}},"required":["createdAt","updatedAt","id","user","level","accessType","invitedBy","accessGrantedAt","status"]},"AddAllOrgUsersToCaseDto":{"type":"object","properties":{"accessLevel":{"type":"string","enum":["owner","admin","commenter","viewer"],"default":"viewer"}},"required":["accessLevel"]},"InviteCaseUsersDto":{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/InviteCaseUser"}}},"required":["users"]},"UserCaseAccessEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"user":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"invitedBy":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"case":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/CaseEntity"}]},"organization":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"status":{"type":"string","example":"active"},"level":{"type":"string","example":"admin"},"accessGrantedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"accessRevokedAt":{"format":"date-time","type":"string","example":null,"nullable":true},"lastAccessedAt":{"format":"date-time","type":"string","example":"2026-01-16T09:12:00.000Z","nullable":true},"isActive":{"type":"boolean","example":true},"isOnline":{"type":"boolean","example":false}},"required":["createdAt","updatedAt","id","user","invitedBy","case","organization","status","level","accessGrantedAt","accessRevokedAt","lastAccessedAt","isActive","isOnline"]},"DeviceConversationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"messageSharing":{"type":"object","example":{"sharedMessageCount":320,"totalMessageCount":1200,"totalReportedAt":"2026-01-15T14:30:00.000Z"}},"selectionCoverage":{"type":"object","example":{"app":"message","conversationId":42,"mode":"selected_messages","selectedMessageCount":320,"eligibleMessageCount":1200,"includedMessageCount":320,"omittedEligibleMessageCount":880,"selectedNotIncludedCount":0,"totalMessageCount":1200,"deviceConversation":"507f1f77bcf86cd799439011","ingestionStatus":"completed","ingestedMessageCount":320,"verifiedAt":"2026-01-15T14:30:00.000Z","completedAt":"2026-01-15T14:30:00.000Z","failedAt":null,"isPartial":true,"coverage":"selected_messages","eligibleScope":"after_date_and_incremental_filters","uploadAttemptId":"a3e6a3ab-2c6e-469c-a3ae-ab1e926c93c2","deviceBackupId":"65a1b2c3d4e5f6a7b8c9d0e2","reportOrder":"2026-01-15T14:30:00.000Z:65a1b2c3d4e5f6a7b8c9d0e2"}},"name":{"type":"string","example":"Jane Doe"},"displayName":{"type":"string","description":"'Conversation with <stored name>' unless the name was edited, in which case the edited name as typed.","example":"Conversation with Jane Doe"},"chatName":{"type":"string","example":"+15555550100"},"originalDisplayName":{"type":"string","example":"Jane Doe"},"source":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"],"example":"message"},"linkedContacts":{"type":"array","items":{"type":"object"},"nullable":true,"example":[],"description":"Not loaded by any route, so always null or an empty list."},"device":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceEntity"}]},"avatar":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileEntity"}]},"service":{"type":"string","example":"iMessage"},"lastMessageText":{"type":"string","example":"See you at 6"},"firstMessageDate":{"format":"date-time","type":"string","example":"2025-03-01T09:00:00.000Z","nullable":true},"lastMessageDate":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"displayNameEditedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"lastMessageExternalId":{"type":"string","example":"58231"},"isGroup":{"type":"boolean","default":false,"example":false},"owner":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"isReadonly":{"type":"boolean","example":false},"lastExportedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"messagesCount":{"type":"number","example":320},"archivedBy":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"archivedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"starredAt":{"format":"date-time","type":"string","nullable":true,"example":null},"tags":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/TagEntity"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}},"isPlaceholder":{"type":"boolean","example":false},"isArchived":{"type":"boolean","example":false},"isStarred":{"type":"boolean","example":false},"hasAllAttachmentProcessed":{"type":"boolean","example":true},"processedAttachmentCount":{"type":"number","example":12},"conversationName":{"type":"string","example":"Conversation with Jane Doe"},"isNameEdited":{"type":"boolean","example":false},"participantCount":{"type":"number","example":2}},"required":["createdAt","updatedAt","id","messageSharing","name","displayName","chatName","originalDisplayName","source","linkedContacts","device","avatar","service","lastMessageText","firstMessageDate","lastMessageDate","displayNameEditedAt","lastMessageExternalId","isGroup","owner","isReadonly","lastExportedAt","messagesCount","archivedBy","archivedAt","starredAt","tags","isPlaceholder","isArchived","isStarred","hasAllAttachmentProcessed","processedAttachmentCount","conversationName","isNameEdited","participantCount"]},"CaseDetailsEntity":{"type":"object","properties":{"case":{"$ref":"#/components/schemas/CaseEntity"},"access":{"$ref":"#/components/schemas/UserCaseAccessEntity"},"usersWithAccess":{"type":"array","items":{"$ref":"#/components/schemas/UserWithCaseAccessEntity"}},"conversations":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/DeviceConversationEntity"}},"noOfBackups":{"type":"number","example":2},"pendingUsersInitials":{"example":["JD"],"type":"array","items":{"type":"string"}},"usersWithPendingAccess":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/CaseInvitationEntity"}},"noOfMembers":{"type":"number","example":4},"noOfPendingUsers":{"type":"number","example":1},"exportCount":{"type":"number","example":3},"conversationsCount":{"type":"number","example":128},"extractionCodes":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeEntity"}},"totalExtractionCodes":{"type":"number","example":5},"dataUsedInBytes":{"type":"number","example":52428800},"totalCaseFileSize":{"type":"number","example":1048576},"hasCaseFiles":{"type":"boolean","example":true},"dataSources":{"type":"array","items":{"$ref":"#/components/schemas/CaseDataSourceEntity"}},"dataSourcesStatusCount":{"type":"object","example":{"completed":4,"in_progress":1}}},"required":["case","access","usersWithAccess","conversations","noOfBackups","pendingUsersInitials","usersWithPendingAccess","noOfMembers","noOfPendingUsers","exportCount","conversationsCount","extractionCodes","totalExtractionCodes","dataUsedInBytes","totalCaseFileSize","hasCaseFiles","dataSources","dataSourcesStatusCount"]},"CaseInvitationAcceptResponseEntity":{"type":"object","properties":{"login":{"$ref":"#/components/schemas/LoginResponseEntity"},"case":{"$ref":"#/components/schemas/CaseDetailsEntity"}},"required":["case"]},"AcceptCaseInvitationDto":{"type":"object","properties":{"token":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"password":{"type":"string"}},"required":["token","email"]},"RejectCaseInvitationRequestDto":{"type":"object","properties":{"token":{"type":"string"},"email":{"type":"string"},"reason":{"type":"string"}},"required":["token","email"]},"UpdateCaseUserAccess":{"type":"object","properties":{"id":{"type":"string"},"accessLevel":{"type":"string","enum":["owner","admin","commenter","viewer"],"default":"viewer"}},"required":["id","accessLevel"]},"UpdateCaseUserAccessDto":{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/UpdateCaseUserAccess"}}},"required":["users"]},"CreateCaseTagDto":{"type":"object","properties":{"name":{"type":"string"},"backgroundColor":{"type":"string"},"textColor":{"type":"string"},"severity":{"type":"string","enum":["info","warning","danger"],"default":"info"}},"required":["name"]},"UpdateCaseTagDto":{"type":"object","properties":{"name":{"type":"string"},"backgroundColor":{"type":"string"},"textColor":{"type":"string"},"severity":{"type":"string","enum":["info","warning","danger"],"default":"info"}}},"CaseCollectionEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Key messages"},"slug":{"type":"string","example":"key-messages"},"case":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"user":{"type":"string","description":"Id of the user who created the collection","example":"507f1f77bcf86cd799439011"},"lastUpdatedBy":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"archivedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"archivedBy":{"type":"string","nullable":true,"example":null}},"required":["createdAt","updatedAt","id","name","slug","case","user","lastUpdatedBy","archivedAt","archivedBy"]},"BulkCollectionCreateResponseEntity":{"type":"object","properties":{"createdCollectionsCount":{"type":"number","example":3}},"required":["createdCollectionsCount"]},"CreateBulkCollectionSearchTermDto":{"type":"object","properties":{"searchTerm":{"type":"string"},"searchFullWord":{"type":"boolean"}},"required":["searchTerm"]},"CreateBulkCollectionsDto":{"type":"object","properties":{"searchTerms":{"type":"array","items":{"$ref":"#/components/schemas/CreateBulkCollectionSearchTermDto"}},"paddingMessagesCount":{"type":"number","description":"Number of messages to add to the beginning and end of the collection","default":0}},"required":["searchTerms"]},"TagResponseEntity":{"type":"object","properties":{"name":{"type":"string","example":"Important"},"nameLowercase":{"type":"string","example":"important"}},"required":["name","nameLowercase"]},"UserFileResponseEntity":{"type":"object","properties":{"name":{"type":"string","example":"IMG_0001.HEIC"},"convertedName":{"type":"string","example":"IMG_0001_display.png","nullable":true},"transferState":{"type":"number","enum":[0,1,5,6,7],"nullable":true,"example":5},"totalBytes":{"type":"number","nullable":true,"example":2483120}},"required":["name","convertedName","transferState","totalBytes"]},"DeviceContactResponseEntity":{"type":"object","properties":{"fullName":{"type":"string","example":"Jane Doe"},"firstName":{"type":"string","example":"Jane"},"phoneNational":{"example":["(555) 555-0100"],"type":"array","items":{"type":"string"}},"avatar":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileResponseEntity"}]},"initials":{"type":"string","example":"JD"},"isEdited":{"type":"boolean","description":"True when the contact name was edited after import. When it was not edited the value is usually false, but because of how it is computed it can also be null, an empty string, or missing.","nullable":true,"example":false}},"required":["fullName","firstName","phoneNational","avatar","initials","isEdited"]},"DeviceMessageResponseEntity":{"type":"object","properties":{"text":{"type":"string","example":"See you at 6","nullable":true},"tags":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/TagResponseEntity"}},"deviceType":{"type":"string","example":"ios","nullable":true},"sender":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceContactResponseEntity"}]},"receiver":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceContactResponseEntity"}]},"isEmpty":{"type":"boolean","example":false},"iMessageType":{"type":"string","enum":["na","text","text-with-attachment","group_title_action","group_picture_action","location_share_start_stop","group_member_added","group_member_removed","face_time","message-layout","location","link","file_attached","unsent"]},"isFromMe":{"type":"boolean","example":true},"isDelivered":{"type":"boolean","example":true},"isDeleted":{"type":"boolean","example":false},"isMissingMedia":{"type":"boolean","example":false},"isSystemMessage":{"type":"boolean","example":false},"attachment":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileResponseEntity"}]},"additionalAttachments":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/UserFileResponseEntity"}},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"sentAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"readAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"deletedAt":{"type":"string","format":"date-time","nullable":true,"example":null},"recoveredAt":{"format":"date-time","type":"string","nullable":true,"example":null},"parentMessageText":{"type":"string","example":"Are we still on for dinner?","description":"Text of the message this one replies to; null when it is not a reply.","nullable":true},"parentMessageAttachment":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/UserFileResponseEntity"}]},"parentMessageIndex":{"type":"number","description":"Index of the message this one replies to; null when it is not a reply.","nullable":true,"example":41},"service":{"type":"string","example":"iMessage"},"mimeType":{"type":"string","example":"image/jpeg"},"editHistory":{"type":"array","items":{"type":"object"},"nullable":true,"example":[{"date":"2026-01-15T14:30:00.000Z","text":"See you at 7"}]},"deletedMessagesLogs":{"type":"object","example":{"total":2,"minDate":"2026-01-15T14:05:00.000Z","maxDate":"2026-01-15T14:20:00.000Z","deletedMessages":[]},"nullable":true},"commentsCount":{"type":"number","example":2},"lastCommentByUserInitials":{"type":"string","example":"JD","nullable":true},"lastCommentByUserAvatarUrl":{"type":"string","example":"https://files.example.com/avatars/jane-doe.png","nullable":true},"messageIndex":{"type":"number","example":42},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}},"ocSharedStatus":{"type":"string","enum":["shared","pending","not_gated"],"nullable":true,"description":"Opposing-council only: how this conversation entry has been shared with the OL. Omitted (null) for non-OC callers."},"collectionCount":{"type":"number","example":1},"durationInSeconds":{"type":"number","description":"Voicemail length in seconds; null unless the message is a voicemail.","nullable":true,"example":32},"callDurationInSeconds":{"type":"number","description":"Call length in seconds; null unless the message is a call.","nullable":true,"example":125},"callType":{"type":"string","description":"Call direction: 'incoming', 'outgoing', 'rejected' or 'not_connected'; an empty string unless the message is a call.","example":"incoming"},"callSource":{"type":"string","nullable":true,"example":"phone"}},"required":["text","tags","deviceType","sender","receiver","isEmpty","iMessageType","isFromMe","isDelivered","isDeleted","isMissingMedia","isSystemMessage","attachment","additionalAttachments","date","sentAt","readAt","deletedAt","recoveredAt","parentMessageText","parentMessageAttachment","parentMessageIndex","service","mimeType","editHistory","deletedMessagesLogs","commentsCount","lastCommentByUserInitials","lastCommentByUserAvatarUrl","messageIndex","collectionCount","durationInSeconds","callDurationInSeconds","callType","callSource"]},"CaseCollectionResponseEntryEntity":{"type":"object","properties":{"deviceMessage":{"$ref":"#/components/schemas/DeviceMessageResponseEntity"},"text":{"type":"string","example":"Key admission"},"customType":{"type":"string","example":null,"nullable":true},"customValue":{"type":"string","example":null,"nullable":true},"position":{"type":"number","description":"Index of the entry in the collection, starts from 0","example":0},"type":{"type":"string","enum":["message","text","line_break","custom"],"default":"message"}},"required":["deviceMessage","text","customType","customValue","position","type"]},"CreateCollectionEntryDto":{"type":"object","properties":{"type":{"type":"string","enum":["message","text","line_break","custom"],"default":"message"},"deviceMessage":{"type":"string","description":"Required, if type is set to message"},"text":{"type":"string","description":"Required if type is set to text"},"customType":{"type":"string","description":"Required if type is set to custom"},"customValue":{"type":"string","description":"Required if type is set to custom"}},"required":["type","deviceMessage","text","customType","customValue"]},"CreateCaseCollectionDto":{"type":"object","properties":{"name":{"type":"string"},"entries":{"type":"array","items":{"$ref":"#/components/schemas/CreateCollectionEntryDto"}}},"required":["name"]},"UpdateCaseCollectionDto":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]},"AddEntriesToCaseCollectionDto":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/CreateCollectionEntryDto"}}},"required":["entries"]},"AddAllMessagesToCollectionDto":{"type":"object","properties":{"sortOrder":{"type":"string","enum":["asc","desc"],"default":"asc"},"conversations":{"description":"Array of conversation ids","maxItems":100,"minItems":1,"type":"array","items":{"type":"string"}},"sortBy":{"type":"string","enum":["sentAt","date"],"default":"date"}},"required":["conversations"]},"RemoveEntriesFromCaseCollectionDto":{"type":"object","properties":{"entries":{"minItems":1,"type":"array","items":{"type":"array"}}},"required":["entries"]},"CaseCollectionEntryEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"deviceMessage":{"type":"string","nullable":true,"description":"Id of the device message this entry points to","example":"507f1f77bcf86cd799439011"},"text":{"type":"string","example":"Key admission","nullable":true},"customType":{"type":"string","example":null,"nullable":true},"customValue":{"type":"string","example":null,"nullable":true},"position":{"type":"number","description":"Index of the entry in the collection, starts from 0","example":0},"type":{"type":"string","enum":["message","text","line_break","custom"],"default":"message"},"createdBy":{"type":"string","example":"507f1f77bcf86cd799439011"},"lastUpdatedBy":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"caseCollection":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"string","example":"507f1f77bcf86cd799439011"}},"required":["createdAt","updatedAt","id","deviceMessage","text","customType","customValue","position","type","createdBy","lastUpdatedBy","caseCollection","case"]},"InsertEntryAtPositionDto":{"type":"object","properties":{"entry":{"$ref":"#/components/schemas/CreateCollectionEntryDto"},"position":{"type":"number","minimum":0}},"required":["entry","position"]},"DeviceContactEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"fullName":{"type":"string","example":"Jane Doe"},"firstName":{"type":"string","example":"Jane"},"lastName":{"type":"string","example":"Doe"},"originalFullName":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane.doe@example.com"},"address":{"type":"string","example":"1 Main St, New York, NY"},"phone":{"example":["+15555550100"],"type":"array","items":{"type":"string"}},"phoneNational":{"example":["(555) 555-0100"],"type":"array","items":{"type":"string"}},"source":{"type":"string","enum":["default","message","device_contact","whatsapp","voicemail","call_log","social","facebook","instagram","threads"]},"linkedContacts":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/DeviceContactEntity"}},"avatar":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileEntity"}]},"device":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceEntity"}]},"owner":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"initials":{"type":"string","example":"JD"},"conversations":{"example":["507f1f77bcf86cd799439014"],"type":"array","items":{"type":"string"}},"isEdited":{"type":"boolean","description":"True when the name differs from the one on the device","example":false}},"required":["createdAt","updatedAt","id","fullName","firstName","lastName","originalFullName","email","address","phone","phoneNational","source","linkedContacts","avatar","device","owner","initials","isEdited"]},"UpdateCaseContactDto":{"type":"object","properties":{"firstName":{"type":"string","maxLength":100,"minLength":3},"middleName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"note":{"type":"string"},"phone":{"type":"array","items":{"type":"string"}}}},"ExportCaseContactsDto":{"type":"object","properties":{"searchText":{"type":"string","maxLength":100,"minLength":3},"conversation":{"type":"string"},"device":{"type":"string"}}},"ArchiveConversationsDto":{"type":"object","properties":{"conversationIds":{"description":"Ids of the conversations to archive","example":["60a0fe4f5311236168a109ca"],"type":"array","items":{"type":"string"}},"deviceId":{"type":"string","description":"The ID of the device associated with the conversation","example":"60a0fe4f5311236168a109cb"},"deleteImmediately":{"type":"boolean","description":"Schedule to delete immediately","example":false,"default":false}},"required":["conversationIds","deviceId","deleteImmediately"]},"UnarchiveConversationsDto":{"type":"object","properties":{"conversationIds":{"description":"Ids of the conversations to unarchive","example":["60a0fe4f5311236168a109ca"],"type":"array","items":{"type":"string"}},"deviceId":{"type":"string","description":"The ID of the device associated with the conversation","example":"60a0fe4f5311236168a109cb"}},"required":["conversationIds","deviceId"]},"EditConversationDto":{"type":"object","properties":{"displayName":{"type":"string"},"isStarred":{"type":"boolean"},"contacts":{"type":"array","items":{"type":"string"}}}},"UpdateCaseFileTagsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Id of the case the file belongs to. Required so @CaseAccess can authorize the caller (an OC included) on the case."},"tags":{"minItems":0,"maxItems":100,"description":"Array of tag ids or tag names. Replaces the existing tags with this list. Pass an empty array to remove all tags.","type":"array","items":{"type":"string"}}},"required":["caseId","tags"]},"AddConversationDto":{"type":"object","properties":{"name":{"type":"string"},"chatName":{"type":"string"},"source":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"type":"string"},"caseId":{"type":"string"},"service":{"type":"string"},"isGroup":{"type":"boolean"},"externalId":{"type":"string"},"externalData":{"type":"object"},"contacts":{"type":"array","items":{"type":"string"}}},"required":["name","chatName","source","deviceId","deviceType","caseId","service","isGroup","externalId"]},"BrowserHistoryVisitsEntity":{"type":"object","properties":{"title":{"type":"string","example":"Example News"},"date":{"format":"date-time","type":"string","example":"2026-01-14T19:40:00.000Z"},"externalId":{"type":"string","example":"91234"}},"required":["title","date","externalId"]},"BrowserHistoryEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"browser":{"type":"string","example":"Safari"},"url":{"type":"string","example":"https://example.com/news","nullable":true},"firstDate":{"format":"date-time","type":"string","example":"2025-11-02T08:15:00.000Z","nullable":true},"lastDate":{"format":"date-time","type":"string","example":"2026-01-14T19:40:00.000Z","nullable":true},"visitCount":{"type":"number","example":17},"externalId":{"type":"string","example":"hist-48213"},"visits":{"type":"array","items":{"$ref":"#/components/schemas/BrowserHistoryVisitsEntity"}}},"required":["createdAt","updatedAt","id","browser","url","firstDate","lastDate","visitCount","externalId","visits"]},"CaseFileEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"contract.pdf"},"type":{"type":"string","example":"document"},"size":{"type":"number","description":"Size in bytes","example":204800},"case":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"file":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileEntity"}]},"extractionCode":{"type":"string","nullable":true,"description":"Id of the extraction code the file was uploaded through","example":"507f1f77bcf86cd799439011"},"category":{"type":"string","nullable":true,"example":"contracts"},"tags":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/TagEntity"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}},"ocSharedStatus":{"type":"string","enum":["shared","pending","not_gated"],"nullable":true,"description":"Opposing-council only: how this file has been shared with the OL. Omitted (null) for non-OC callers, and for legal-integration documents (a separate OC gate not yet wired into this field)."}},"required":["createdAt","updatedAt","id","name","type","size","case","file","extractionCode","category","tags"]},"SingleAddCaseFilesDto":{"type":"object","properties":{"file":{"type":"string"},"type":{"type":"string"},"category":{"type":"string"}},"required":["file"]},"AddCaseFilesViaCaseDto":{"type":"object","properties":{"files":{"type":"array","items":{"$ref":"#/components/schemas/SingleAddCaseFilesDto"}}},"required":["files"]},"RequestCaseFilesZipDownloadDto":{"type":"object","properties":{"searchText":{"type":"string"},"extractionCode":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"category":{"type":"string"},"fileIds":{"description":"CaseFile ObjectIds to include in the zip. If non-empty, takes precedence over filter fields. Maximum 50000 ids.","type":"array","items":{"type":"string"}}}},"UpdateCaseFileCategoryDto":{"type":"object","properties":{"category":{"type":"string","nullable":true}}},"DeviceMediaEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"externalId":{"type":"number","example":48213},"fileName":{"type":"string","example":"IMG_0001.HEIC"},"fileCreatedAt":{"format":"date-time","type":"string","example":"2025-06-01T10:00:00.000Z","nullable":true},"type":{"type":"number","example":1,"nullable":true},"duration":{"type":"number","example":0,"nullable":true},"uniformType":{"type":"string","example":"public.heic","nullable":true},"trashedState":{"type":"number","example":0,"nullable":true},"album":{"type":"object","example":{"id":1,"name":"Camera Roll"},"nullable":true},"originalFile":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileEntity"}]},"thumbnail":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileEntity"}]},"metadata":{"type":"object","example":{}}},"required":["createdAt","updatedAt","id","externalId","fileName","originalFile","thumbnail"]},"DeviceNotesEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"app":{"type":"string","example":"Notes"},"title":{"type":"string","example":"To do","nullable":true},"summary":{"type":"string","example":"Call Sam about the invoice","nullable":true},"rawData":{"type":"string","example":"Call Sam about the invoice","nullable":true},"htmlData":{"type":"string","example":"<p>Call Sam about the invoice</p>","nullable":true},"creationDate":{"format":"date-time","type":"string","example":"2025-05-20T08:00:00.000Z","nullable":true},"modificationDate":{"format":"date-time","type":"string","example":"2025-05-21T09:30:00.000Z","nullable":true},"isDeleted":{"type":"boolean","example":false},"externalId":{"type":"string","example":"note-7731"},"attachments":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/UserFileEntity"}}},"required":["createdAt","updatedAt","id","app","title","summary","rawData","htmlData","creationDate","modificationDate","isDeleted","externalId","attachments"]},"ExtractionCodeSuggestionMessageEntity":{"type":"object","properties":{"text":{"type":"string","example":"Please include WhatsApp."},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:35:00.000Z"},"user":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439013"},"statusUpdate":{"type":"string","enum":["open","approved","rejected","closed"],"nullable":true}},"required":["text","date","user"]},"ExtractionCodeSuggestionEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"status":{"type":"string","enum":["open","approved","rejected","closed"]},"suggestion":{"type":"object","example":{"type":"share_all"}},"previousConfig":{"type":"object","nullable":true,"example":{"type":"allow_client"}},"extractionCode":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439012"},"case":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"suggestedBy":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439013"},"suggestedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"approvedBy":{"type":"string","nullable":true,"example":null},"approvedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"rejectedBy":{"type":"string","nullable":true,"example":null},"rejectedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"mergedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeSuggestionMessageEntity"}}},"required":["createdAt","updatedAt","id","status","suggestion","previousConfig","extractionCode","case","suggestedBy","suggestedAt","approvedBy","approvedAt","rejectedBy","rejectedAt","mergedAt","messages"]},"CreateExtractionCodeSuggestionDto":{"type":"object","properties":{"suggestion":{"$ref":"#/components/schemas/ExtractionCodeConfigDto"},"message":{"type":"string","maxLength":2000,"description":"Optional message to attach alongside the suggestion"}},"required":["suggestion"]},"UpdateExtractionCodeSuggestionDto":{"type":"object","properties":{"suggestion":{"$ref":"#/components/schemas/ExtractionCodeConfigDto"}},"required":["suggestion"]},"CreateExtractionCodeSuggestionMessageDto":{"type":"object","properties":{"text":{"type":"string","maxLength":2000}},"required":["text"]},"RejectExtractionCodeSuggestionDto":{"type":"object","properties":{"message":{"type":"string","maxLength":2000}}},"ReviewExtractionCodeDto":{"type":"object","properties":{"message":{"type":"string","description":"Optional view-only note saved on the code alongside the review decision"}}},"OcDataApprovalEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"dataProviderType":{"type":"string","enum":["Device","UserEmailDataProvider","CaseFileProvider","FinancialAccountProvider","LegalDocumentProvider","DiscordUserProvider","RedditUserProvider","LinkedInUserProvider","AiChatProvider"]},"dataProvider":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439014"},"mode":{"type":"string","enum":["approve_all","approve_subset"]},"individualDataIds":{"example":[],"type":"array","items":{"type":"string"}},"case":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"originatingOrg":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439016"},"opposingCouncilOrg":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439015"},"approvedBy":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439013"},"approvedAt":{"format":"date-time","type":"string","example":"2026-01-16T10:00:00.000Z"}},"required":["createdAt","updatedAt","id","dataProviderType","dataProvider","mode","individualDataIds","case","originatingOrg","opposingCouncilOrg","approvedBy","approvedAt"]},"OcDataApprovalRequestDto":{"type":"object","properties":{"dataProviderType":{"type":"string","enum":["Device","UserEmailDataProvider","CaseFileProvider","FinancialAccountProvider","LegalDocumentProvider","DiscordUserProvider","RedditUserProvider","LinkedInUserProvider","AiChatProvider"],"description":"Type of data provider the approval applies to"},"dataProviderId":{"type":"string","description":"Id of the specific data provider document"},"individualIds":{"description":"Ids of individual data items. Empty/omitted means \"all\" for approve, or \"deny all\" for reject.","type":"array","items":{"type":"string"}}},"required":["dataProviderType","dataProviderId"]},"InviteOcColleagueDto":{"type":"object","properties":{"email":{"type":"string","description":"Email of the OCG colleague to add"},"name":{"type":"string"}},"required":["email"]},"IngestEventResultEntity":{"type":"object","properties":{"eventId":{"type":"string","nullable":true,"example":"7f3c2a10-5b4e-4d2a-9c1e-2f6b8a9d0e11"},"status":{"type":"string","enum":["accepted","duplicate","rejected","retry"],"example":"accepted"},"reason":{"type":"string"}},"required":["eventId","status"]},"IngestCollectionEventsResponseEntity":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/IngestEventResultEntity"}}},"required":["results"]},"IngestCollectionEventsDto":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object"}}},"required":["events"]},"SnoozeInboxItemDto":{"type":"object","properties":{"until":{"type":"string","format":"date-time"}},"required":["until"]},"SetWatchedDto":{"type":"object","properties":{"watched":{"type":"boolean"}},"required":["watched"]},"SetOrgPriorityDto":{"type":"object","properties":{"isPriority":{"type":"boolean"}},"required":["isPriority"]},"CertificateEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"certificateId":{"type":"string","example":"HC-2026-05-15-A1B2-1F3"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"sourceIds":{"example":["65a1b2c3d4e5f6a7b8c9d0f4"],"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["verified","partial","revoked"],"example":"verified"},"generatedBySide":{"type":"string","enum":["org","opposing_council"],"example":"org"},"certificate":{"type":"object","example":{"generatedAt":"2026-05-15T12:00:00.000Z","dataSummary":{}}},"revokedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"revocationReason":{"type":"string","nullable":true,"example":null},"createdBy":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e4"}},"required":["createdAt","updatedAt","id","certificateId","organization","case","sourceIds","status","generatedBySide","certificate","revokedAt","revocationReason","createdBy"]},"CreateCertificateDto":{"type":"object","properties":{"certificateId":{"type":"string"},"certificate":{"type":"object"},"pdfBase64":{"type":"string"},"jsonString":{"type":"string"},"caseId":{"type":"string"},"sourceIds":{"type":"array","items":{"type":"string"}}},"required":["certificateId","certificate","pdfBase64","jsonString","caseId"]},"DocumentVerificationEntity":{"type":"object","properties":{"status":{"type":"string","example":"authentic"},"certificateId":{"type":"string","example":"HC-2026-05-15-A1B2-1F3"},"certificateStatus":{"type":"string","enum":["verified","partial","revoked"],"example":"verified"},"signaturePresent":{"type":"boolean","example":true},"hashMatch":{"type":"boolean","example":true},"documentMatch":{"type":"boolean","example":true},"issuedAt":{"type":"string","nullable":true,"example":"2026-05-15T09:30:00.000Z"},"message":{"type":"string","example":"This document is an authentic, unmodified certificate issued by Hearsay."}},"required":["status","certificateId","certificateStatus","signaturePresent","hashMatch","documentMatch","issuedAt","message"]},"CertificateVerificationEntity":{"type":"object","properties":{"selectedMessageUpload":{"type":"object","example":{"version":1,"conversations":[{"messageSharing":{"sharedMessageCount":25,"totalMessageCount":480,"totalReportedAt":"2026-05-14T16:05:00.000Z"},"selectionCoverage":{"app":"message","conversationId":42,"mode":"selected_messages","selectedMessageCount":25,"eligibleMessageCount":480,"includedMessageCount":25,"omittedEligibleMessageCount":455,"selectedNotIncludedCount":0,"totalMessageCount":480,"deviceConversation":"507f1f77bcf86cd799439011","ingestionStatus":"completed","ingestedMessageCount":25,"verifiedAt":"2026-05-14T16:10:00.000Z","completedAt":"2026-05-14T16:10:00.000Z","failedAt":null,"isPartial":true,"coverage":"selected_messages","eligibleScope":"after_date_and_incremental_filters","uploadAttemptId":"3f2b8c1e-9a4d-4e7b-8c2f-1d5e6a7b8c9d","deviceBackupId":"65a1b2c3d4e5f6a7b8c9d0e2","reportOrder":"2026-05-14T16:00:00.000Z:65a1b2c3d4e5f6a7b8c9d0e2"}}]}},"certificateId":{"type":"string","example":"HC-2026-05-15-A1B2-1F3"},"status":{"type":"string","enum":["verified","partial","revoked"],"example":"verified"},"generatedBySide":{"type":"string","enum":["org","opposing_council"],"example":"org"},"collectionDate":{"type":"string","nullable":true,"example":"2026-05-14T16:02:00.000Z"},"custodianName":{"type":"string","nullable":true,"example":"Jane Doe"},"collectionId":{"type":"string","nullable":true,"example":"A1B2C3"},"dataHash":{"type":"string","nullable":true,"example":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},"itemsCollected":{"type":"number","nullable":true,"example":1520},"issuedAt":{"type":"string","nullable":true,"example":"2026-05-15T09:30:00.000Z"},"requestingAttorney":{"type":"string","nullable":true,"example":"John Smith"},"deviceName":{"type":"string","nullable":true,"example":"iPhone"},"deviceModel":{"type":"string","nullable":true,"example":"iPhone 15"},"conversationCount":{"type":"number","nullable":true,"example":12},"totalMessages":{"type":"number","nullable":true,"example":1480},"dataDateRange":{"type":"object","nullable":true,"example":{"start":"2025-01-01","end":"2026-05-14"}},"extractionCode":{"type":"string","nullable":true,"example":"A1B2C3"}},"required":["certificateId","status","generatedBySide","collectionDate","custodianName","collectionId","dataHash","itemsCollected","issuedAt","requestingAttorney","deviceName","deviceModel","conversationCount","totalMessages","dataDateRange","extractionCode"]},"CertificateListEntity":{"type":"object","properties":{"total":{"type":"number","example":42},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20},"totalPages":{"type":"number","example":3},"nextPage":{"type":"number","example":2,"nullable":true},"previousPage":{"type":"number","example":null,"nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/CertificateEntity"}}},"required":["total","totalPages","nextPage","previousPage","data"]},"RevokeCertificateDto":{"type":"object","properties":{"revocationReason":{"type":"string"}},"required":["revocationReason"]},"MessageSelectionConversationDto":{"type":"object","properties":{"app":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump"]},"conversationId":{"type":"number"},"mode":{"type":"string"},"selectedMessageCount":{"type":"number"},"eligibleMessageCount":{"type":"number","nullable":true},"includedMessageCount":{"type":"number"},"omittedEligibleMessageCount":{"type":"number","nullable":true},"selectedNotIncludedCount":{"type":"number"},"totalMessageCount":{"type":"number","nullable":true}},"required":["app","conversationId","mode","selectedMessageCount","eligibleMessageCount","includedMessageCount","omittedEligibleMessageCount","selectedNotIncludedCount"]},"PreparedMessageSelectionReportDto":{"type":"object","properties":{"version":{"type":"number"},"uploadAttemptId":{"type":"string"},"phase":{"type":"object"},"platformImportAccepted":{"type":"boolean"},"coverage":{"type":"string"},"eligibleScope":{"type":"string"},"conversations":{"type":"array","items":{"$ref":"#/components/schemas/MessageSelectionConversationDto"}}},"required":["version","uploadAttemptId","phase","platformImportAccepted","coverage","eligibleScope","conversations"]},"UploadBulkConversationDto":{"type":"object","properties":{"messageSelectionReport":{"$ref":"#/components/schemas/PreparedMessageSelectionReportDto"},"systemId":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"dateAppended":{"type":"string","example":"DD_MM_YY_HH_mm_ss"},"organizationId":{"type":"string"},"extractionCode":{"type":"string"},"case":{"type":"string"},"shareEmails":{"type":"array","items":{"type":"string"}},"totalSize":{"type":"number"},"appVersion":{"type":"string"},"localBackupPath":{"type":"string"},"timezone":{"type":"string"},"searchData":{"type":"array","items":{"type":"string"}},"messagesAlreadyExists":{"type":"boolean"},"isShareAll":{"type":"boolean"}},"required":["systemId","deviceId","deviceType","dateAppended","organizationId"]},"SearchEntryMessageResponseEntity":{"type":"object","properties":{"text":{"type":"string","example":"See you at 6","nullable":true},"tags":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/TagResponseEntity"}},"deviceType":{"type":"string","example":"ios","nullable":true},"sender":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceContactResponseEntity"}]},"receiver":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceContactResponseEntity"}]},"isEmpty":{"type":"boolean","example":false},"iMessageType":{"type":"string","enum":["na","text","text-with-attachment","group_title_action","group_picture_action","location_share_start_stop","group_member_added","group_member_removed","face_time","message-layout","location","link","file_attached","unsent"]},"isFromMe":{"type":"boolean","example":true},"isDelivered":{"type":"boolean","example":true},"isDeleted":{"type":"boolean","example":false},"isMissingMedia":{"type":"boolean","example":false},"isSystemMessage":{"type":"boolean","example":false},"attachment":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileResponseEntity"}]},"additionalAttachments":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/UserFileResponseEntity"}},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"sentAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"readAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"deletedAt":{"type":"string","format":"date-time","nullable":true,"example":null},"recoveredAt":{"format":"date-time","type":"string","nullable":true,"example":null},"parentMessageText":{"type":"string","example":"Are we still on for dinner?","description":"Text of the message this one replies to; null when it is not a reply.","nullable":true},"parentMessageAttachment":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/UserFileResponseEntity"}]},"parentMessageIndex":{"type":"number","description":"Index of the message this one replies to; null when it is not a reply.","nullable":true,"example":41},"service":{"type":"string","example":"iMessage"},"mimeType":{"type":"string","example":"image/jpeg"},"editHistory":{"type":"array","items":{"type":"object"},"nullable":true,"example":[{"date":"2026-01-15T14:30:00.000Z","text":"See you at 7"}]},"deletedMessagesLogs":{"type":"object","example":{"total":2,"minDate":"2026-01-15T14:05:00.000Z","maxDate":"2026-01-15T14:20:00.000Z","deletedMessages":[]},"nullable":true},"commentsCount":{"type":"number","example":2},"messageIndex":{"type":"number","example":42},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}},"ocSharedStatus":{"type":"string","enum":["shared","pending","not_gated"],"nullable":true,"description":"Opposing-council only: how this conversation entry has been shared with the OL. Omitted (null) for non-OC callers."},"collectionCount":{"type":"number","example":1},"durationInSeconds":{"type":"number","description":"Voicemail length in seconds; null unless the message is a voicemail.","nullable":true,"example":32},"callDurationInSeconds":{"type":"number","description":"Call length in seconds; null unless the message is a call.","nullable":true,"example":125},"callType":{"type":"string","description":"Call direction: 'incoming', 'outgoing', 'rejected' or 'not_connected'; an empty string unless the message is a call.","example":"incoming"},"callSource":{"type":"string","nullable":true,"example":"phone"}},"required":["text","tags","deviceType","sender","receiver","isEmpty","iMessageType","isFromMe","isDelivered","isDeleted","isMissingMedia","isSystemMessage","attachment","additionalAttachments","date","sentAt","readAt","deletedAt","recoveredAt","parentMessageText","parentMessageAttachment","parentMessageIndex","service","mimeType","editHistory","deletedMessagesLogs","commentsCount","messageIndex","collectionCount","durationInSeconds","callDurationInSeconds","callType","callSource"]},"CompareDeviceMessageResponseEntity":{"type":"object","properties":{"text":{"type":"string","example":"See you at 6","nullable":true},"tags":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/TagResponseEntity"}},"deviceType":{"type":"string","example":"ios","nullable":true},"sender":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceContactResponseEntity"}]},"receiver":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceContactResponseEntity"}]},"isEmpty":{"type":"boolean","example":false},"iMessageType":{"type":"string","enum":["na","text","text-with-attachment","group_title_action","group_picture_action","location_share_start_stop","group_member_added","group_member_removed","face_time","message-layout","location","link","file_attached","unsent"]},"isFromMe":{"type":"boolean","example":true},"isDelivered":{"type":"boolean","example":true},"isDeleted":{"type":"boolean","example":false},"isMissingMedia":{"type":"boolean","example":false},"isSystemMessage":{"type":"boolean","example":false},"attachment":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileResponseEntity"}]},"additionalAttachments":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/UserFileResponseEntity"}},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"sentAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"readAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"deletedAt":{"type":"string","format":"date-time","nullable":true,"example":null},"recoveredAt":{"format":"date-time","type":"string","nullable":true,"example":null},"parentMessageText":{"type":"string","example":"Are we still on for dinner?","description":"Text of the message this one replies to; null when it is not a reply.","nullable":true},"parentMessageAttachment":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/UserFileResponseEntity"}]},"parentMessageIndex":{"type":"number","description":"Index of the message this one replies to; null when it is not a reply.","nullable":true,"example":41},"service":{"type":"string","example":"iMessage"},"mimeType":{"type":"string","example":"image/jpeg"},"editHistory":{"type":"array","items":{"type":"object"},"nullable":true,"example":[{"date":"2026-01-15T14:30:00.000Z","text":"See you at 7"}]},"deletedMessagesLogs":{"type":"object","example":{"total":2,"minDate":"2026-01-15T14:05:00.000Z","maxDate":"2026-01-15T14:20:00.000Z","deletedMessages":[]},"nullable":true},"messageIndex":{"type":"number","example":42},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}},"ocSharedStatus":{"type":"string","enum":["shared","pending","not_gated"],"nullable":true,"description":"Opposing-council only: how this conversation entry has been shared with the OL. Omitted (null) for non-OC callers."},"collectionCount":{"type":"number","example":1},"durationInSeconds":{"type":"number","description":"Voicemail length in seconds; null unless the message is a voicemail.","nullable":true,"example":32},"callDurationInSeconds":{"type":"number","description":"Call length in seconds; null unless the message is a call.","nullable":true,"example":125},"callType":{"type":"string","description":"Call direction: 'incoming', 'outgoing', 'rejected' or 'not_connected'; an empty string unless the message is a call.","example":"incoming"},"callSource":{"type":"string","nullable":true,"example":"phone"},"conversationId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"}},"required":["text","tags","deviceType","sender","receiver","isEmpty","iMessageType","isFromMe","isDelivered","isDeleted","isMissingMedia","isSystemMessage","attachment","additionalAttachments","date","sentAt","readAt","deletedAt","recoveredAt","parentMessageText","parentMessageAttachment","parentMessageIndex","service","mimeType","editHistory","deletedMessagesLogs","messageIndex","collectionCount","durationInSeconds","callDurationInSeconds","callType","callSource","conversationId"]},"CompareConversationMessagesDto":{"type":"object","properties":{"case":{"type":"string","description":"Required if caseSlug not present"},"caseSlug":{"type":"string","description":"Required if case not present"},"conversations":{"description":"ID of the conversations to compare","type":"array","items":{"type":"string"}},"startDate":{"format":"date-time","type":"string"},"endDate":{"format":"date-time","type":"string"},"sortDirection":{"type":"string","enum":["asc","desc"],"default":"asc"},"nextToken":{"type":"string"}},"required":["conversations"]},"DeviceMessageTextSearchResponse":{"type":"object","properties":{"text":{"type":"string","example":"See you at 6","nullable":true},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TagResponseEntity"}},"deviceConversation":{"$ref":"#/components/schemas/DeviceConversationEntity"},"deviceType":{"type":"string","example":"ios","nullable":true},"sender":{"$ref":"#/components/schemas/DeviceContactResponseEntity"},"receiver":{"$ref":"#/components/schemas/DeviceContactResponseEntity"},"isEmpty":{"type":"boolean","example":false},"iMessageType":{"type":"string","enum":["na","text","text-with-attachment","group_title_action","group_picture_action","location_share_start_stop","group_member_added","group_member_removed","face_time","message-layout","location","link","file_attached","unsent"]},"isFromMe":{"type":"boolean","example":true},"isDelivered":{"type":"boolean","example":true},"isDeleted":{"type":"boolean","example":false},"isMissingMedia":{"type":"boolean","example":false},"isSystemMessage":{"type":"boolean","example":false},"attachment":{"$ref":"#/components/schemas/UserFileResponseEntity"},"additionalAttachments":{"$ref":"#/components/schemas/UserFileResponseEntity"},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"sentAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"readAt":{"format":"date-time","type":"string","example":null,"nullable":true},"deletedAt":{"type":"string","format":"date-time","nullable":true,"example":null},"recoveredAt":{"format":"date-time","type":"string","example":null,"nullable":true},"parentMessageText":{"type":"string","example":"Are we still on for tonight?","description":"Text of the message this one replies to; null when it is not a reply.","nullable":true},"parentMessageAttachment":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/UserFileResponseEntity"}]},"parentMessageIndex":{"type":"number","description":"Index of the message this one replies to; null when it is not a reply.","nullable":true,"example":41},"service":{"type":"string","example":"iMessage"},"mimeType":{"type":"string","example":"image/jpeg"},"editHistory":{"type":"array","items":{"type":"object"},"nullable":true,"example":[{"date":"2026-01-15T14:30:00.000Z","text":"See you at 7"}]},"deletedMessagesLogs":{"type":"object","example":{"total":3,"minDate":"2026-01-14T09:00:00.000Z","maxDate":"2026-01-15T14:30:00.000Z","deletedMessages":[]}},"messageIndex":{"type":"number","example":42},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}},"collectionCount":{"type":"number","example":1},"durationInSeconds":{"type":"number","description":"Voicemail length in seconds; null unless the message is a voicemail.","nullable":true,"example":37},"callDurationInSeconds":{"type":"number","description":"Call length in seconds; null unless the message is a call.","nullable":true,"example":125},"callType":{"type":"string","description":"Call direction: 'incoming', 'outgoing', 'rejected' or 'not_connected'; an empty string unless the message is a call.","example":"outgoing"},"highlightedText":{"type":"string","example":"See you at [highlight=#FFFF00]6[/highlight]"}},"required":["text","tags","deviceConversation","deviceType","sender","receiver","isEmpty","iMessageType","isFromMe","isDelivered","isDeleted","isMissingMedia","isSystemMessage","attachment","additionalAttachments","date","sentAt","readAt","deletedAt","recoveredAt","parentMessageText","parentMessageAttachment","parentMessageIndex","service","mimeType","editHistory","deletedMessagesLogs","messageIndex","collectionCount","durationInSeconds","callDurationInSeconds","callType","highlightedText"]},"AdvanceTextSearchDto":{"type":"object","properties":{"page":{"type":"number","default":1},"perPage":{"type":"number","default":10},"skip":{"type":"number","description":"If skip/limit not provided, page/perPage will take precedence"},"limit":{"type":"number","description":"If skip/limit not provided, page/perPage will take precedence"},"case":{"type":"string","description":"Required if caseSlug not present"},"caseSlug":{"type":"string","description":"Required if case not present"},"keywords":{"type":"array","items":{"type":"array","maxLength":100,"minLength":3}},"linkedContacts":{"type":"array","items":{"type":"string"}},"devices":{"type":"array","items":{"type":"string"}},"startDate":{"format":"date-time","type":"string"},"endDate":{"format":"date-time","type":"string"},"bbCode":{"type":"string","default":"highlight=\"#FFFF00\""},"operator":{"type":"string","enum":["AND","OR"],"default":"AND"},"matchType":{"type":"string","enum":["FUZZY","EXACT"],"default":"FUZZY"}},"required":["keywords"]},"MessageTagCountEntity":{"type":"object","properties":{"tag":{"type":"string","description":"Tag name.","example":"Important"},"count":{"type":"number","description":"Number of messages carrying this tag.","example":12}},"required":["tag","count"]},"MessageTagsCountResponseEntity":{"type":"object","properties":{"total":{"type":"number","example":42},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20},"totalPages":{"type":"number","example":3},"nextPage":{"type":"number","example":2,"nullable":true},"previousPage":{"type":"number","example":null,"nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/MessageTagCountEntity"}}},"required":["total","totalPages","nextPage","previousPage","data"]},"MessageTagsCountDto":{"type":"object","properties":{"page":{"type":"number","default":1},"perPage":{"type":"number","default":10},"skip":{"type":"number","description":"If skip/limit not provided, page/perPage will take precedence"},"limit":{"type":"number","description":"If skip/limit not provided, page/perPage will take precedence"},"conversationIds":{"maxItems":500,"description":"Restrict to these conversation ids (must belong to the case). Omit to cover every conversation in the case.","type":"array","items":{"type":"string"}},"devices":{"maxItems":50,"description":"Restrict to conversations on these device ids.","type":"array","items":{"type":"string"}},"sources":{"type":"array","maxItems":8,"description":"Restrict to conversations of these sources.","items":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"]}}}},"UploadedConversation":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"messageSharing":{"type":"object"},"selectionCoverage":{"type":"object"},"name":{"type":"string"},"messagesCount":{"type":"number"},"displayName":{"type":"string"},"chatName":{"type":"string"},"source":{"type":"string","enum":["message","whatsapp","voicemail","call_log","facebook_dump","instagram_dump","threads_dump","email"],"example":"message"},"lastMessageText":{"type":"string"},"lastMessageDate":{"format":"date-time","type":"string"},"lastMessageExternalId":{"type":"string"},"isGroup":{"type":"boolean","default":false},"externalId":{"type":"string"},"deviceExternalId":{"type":"string"},"callLogNumber":{"type":"string"}},"required":["createdAt","updatedAt","id","messageSharing","name","messagesCount","displayName","chatName","source","lastMessageText","lastMessageDate","lastMessageExternalId","isGroup","externalId","deviceExternalId","callLogNumber"]},"SearchMessageByDateResponseEntity":{"type":"object","properties":{"messageIndex":{"type":"number","example":42},"messageId":{"type":"string","example":"507f1f77bcf86cd799439011"},"date":{"type":"string","example":"2026-01-15T14:30:00.000Z"}},"required":["messageIndex","messageId","date"]},"DeviceMessageReactionsEntity":{"type":"object","properties":{"reaction":{"type":"string","example":"👍","nullable":true},"contact":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."}},"required":["contact"]},"DeviceMessageEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"text":{"type":"string","example":"See you at 6","nullable":true},"tags":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/TagEntity"}},"deviceConversation":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceConversationEntity"}]},"device":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"deviceType":{"type":"string","example":"ios","nullable":true},"sender":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceContactEntity"}]},"receiver":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceContactEntity"}]},"isEmpty":{"type":"boolean","example":false},"iMessageType":{"type":"string","enum":["na","text","text-with-attachment","group_title_action","group_picture_action","location_share_start_stop","group_member_added","group_member_removed","face_time","message-layout","location","link","file_attached","unsent"]},"isFromMe":{"type":"boolean","example":true},"isDelivered":{"type":"boolean","example":true},"isDeleted":{"type":"boolean","example":false},"isFinished":{"type":"boolean","example":false},"isArchive":{"type":"boolean","example":false},"isSpam":{"type":"boolean","example":false},"isSticker":{"type":"boolean","example":false},"isMissingMedia":{"type":"boolean","example":false},"isSystemMessage":{"type":"boolean","example":false},"attachment":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserFileEntity"}]},"additionalAttachments":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/UserFileEntity"}},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"sentAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"readAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"playedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"editedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"deliveredAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"deletedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"recoveredAt":{"format":"date-time","type":"string","nullable":true,"example":null},"retractedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"parentMessage":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceMessageEntity"}]},"messageFrom":{"type":"string","example":"+15555550100"},"messageTo":{"type":"string","example":"+15555550101"},"service":{"type":"string","example":"iMessage"},"mimeType":{"type":"string","example":"image/jpeg"},"commentsCount":{"type":"number","example":2},"lastCommentByUserInitials":{"type":"string","example":"JD","nullable":true},"lastCommentByUserAvatarUrl":{"type":"string","example":"https://files.example.com/avatars/jane-doe.png","nullable":true},"messageIndex":{"type":"number","example":42},"editHistory":{"type":"array","items":{"type":"object"},"nullable":true,"example":[{"date":"2026-01-15T14:30:00.000Z","text":"See you at 7"}]},"deletedMessagesLogs":{"type":"object","example":{"total":2,"deletedMessages":[{"ids":[1041,1042],"startDate":"2026-01-15T14:05:00.000Z","endDate":"2026-01-15T14:20:00.000Z","count":2,"previousId":1040,"nextId":1043,"prevChatId":12,"nextChatId":12,"isPrevMessageInSameChat":true,"isNextMessageInSameChat":true}],"minDate":"2026-01-15T14:05:00.000Z","maxDate":"2026-01-15T14:20:00.000Z"}},"guid":{"type":"string","example":"9F1C2B3A-4D5E-4F60-8A7B-1C2D3E4F5A6B","nullable":true},"reactions":{"type":"array","items":{"$ref":"#/components/schemas/DeviceMessageReactionsEntity"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}},"collectionCount":{"type":"number","example":1},"durationInSeconds":{"type":"number","example":32},"callDurationInSeconds":{"type":"number","example":125},"callType":{"type":"string","example":"incoming"}},"required":["createdAt","updatedAt","id","text","tags","deviceConversation","device","deviceType","sender","receiver","isEmpty","iMessageType","isFromMe","isDelivered","isDeleted","isFinished","isArchive","isSpam","isSticker","isMissingMedia","isSystemMessage","attachment","additionalAttachments","date","sentAt","readAt","playedAt","editedAt","deliveredAt","deletedAt","recoveredAt","retractedAt","parentMessage","messageFrom","messageTo","service","mimeType","lastCommentByUserInitials","lastCommentByUserAvatarUrl","messageIndex","editHistory","deletedMessagesLogs","guid","reactions","collectionCount","durationInSeconds","callDurationInSeconds","callType"]},"UpdateBulkMessageTagsResultEntity":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DeviceMessageEntity"}},"required":["data"]},"UpdateBulkMessageTagsDto":{"type":"object","properties":{"messages":{"minItems":0,"maxItems":1000,"description":"Array of message ids","type":"array","items":{"type":"string"}},"tagNames":{"minItems":0,"maxItems":200,"description":"array of tag text or id","type":"array","items":{"type":"string"}}},"required":["messages","tagNames"]},"UpdateMessageTagsDto":{"type":"object","properties":{"tagNames":{"minItems":0,"maxItems":100,"description":"Array of tag text or id","type":"array","items":{"type":"string"}}},"required":["tagNames"]},"RemoveMessageTagsDto":{"type":"object","properties":{"tagNames":{"minItems":0,"maxItems":100,"description":"Array of tag text or id","type":"array","items":{"type":"string"}}},"required":["tagNames"]},"AwsTemporaryS3CredentialEntity":{"type":"object","properties":{"accessKeyId":{"type":"string","example":"example-access-key-id"},"secretAccessKey":{"type":"string","example":"example-secret-access-key"},"sessionToken":{"type":"string","example":"example-session-token"},"region":{"type":"string","example":"us-east-1"},"bucket":{"type":"string","example":"acme-user-data"},"apiVersion":{"type":"string","example":"2006-03-01"},"expiration":{"format":"date-time","type":"string","example":"2026-01-15T11:15:00.000Z"}},"required":["accessKeyId","secretAccessKey","sessionToken","region","bucket","apiVersion","expiration"]},"AddFileDto":{"type":"object","properties":{"fileKey":{"type":"string","description":"S3 file key (path in bucket)"},"type":{"type":"string","description":"Override content type (e.g. \"application/pdf\"). If not provided, uses the content type from S3."}},"required":["fileKey"]},"AppFileLink":{"type":"object","properties":{"url":{"type":"string","example":"Hearsay-3.14.0-arm64.dmg"},"sha512":{"type":"string","example":"q8Y0bYF0dGVzdGluZ0V4YW1wbGVTaGE1MTJCYXNlNjRWYWx1ZUZvckRvY3NPbmx5MDAwMDAwMDAwMDA9PQ=="},"size":{"type":"number","example":152043520}},"required":["url","sha512","size"]},"LatestAppLinkForOsEntity":{"type":"object","properties":{"version":{"type":"string","example":"3.14.0"},"files":{"type":"array","items":{"$ref":"#/components/schemas/AppFileLink"}}},"required":["version","files"]},"LatestAppLinksEntity":{"type":"object","properties":{"mac":{"$ref":"#/components/schemas/LatestAppLinkForOsEntity"},"windows":{"$ref":"#/components/schemas/LatestAppLinkForOsEntity"}},"required":["mac","windows"]},"DeviceFileHashDto":{"type":"object","properties":{"fileName":{"type":"string"},"fileHash":{"type":"string"}},"required":["fileName","fileHash"]},"AddUpdateDeviceFileHashDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"systemId":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/DeviceFileHashDto"}},"appVersion":{"type":"string","description":"App version"}},"required":["deviceId","deviceType","systemId","files"]},"MatchDeviceFileHashResultEntity":{"type":"object","properties":{"matched":{"type":"boolean","example":true}},"required":["matched"]},"MatchDeviceFileHashDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"systemId":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/DeviceFileHashDto"}},"appVersion":{"type":"string","description":"App version"}},"required":["deviceId","deviceType","systemId","files"]},"UpdateDeviceDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the device"},"manufacturer":{"type":"string","description":"Manufacturer of the device"},"brand":{"type":"string","description":"Brand of the device"},"model":{"type":"string","description":"Model of the device"}}},"MessageSelectionReportDto":{"type":"object","properties":{"version":{"type":"number"},"uploadAttemptId":{"type":"string"},"phase":{"type":"object"},"platformImportAccepted":{"type":"boolean"},"coverage":{"type":"string"},"eligibleScope":{"type":"string"},"conversations":{"type":"array","items":{"$ref":"#/components/schemas/MessageSelectionConversationDto"}}},"required":["version","uploadAttemptId","phase","platformImportAccepted","coverage","eligibleScope","conversations"]},"OsDetailsDto":{"type":"object","properties":{"arch":{"type":"string"},"platform":{"type":"string"},"version":{"type":"string"},"osType":{"type":"string"},"machine":{"type":"string"},"totalMemory":{"type":"number"},"cpus":{"type":"object"},"extraInformation":{"type":"object"}},"required":["cpus","extraInformation"]},"AddDeviceActivityLogDto":{"type":"object","properties":{"messageSelectionReport":{"$ref":"#/components/schemas/MessageSelectionReportDto"},"systemId":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"type":{"type":"string","enum":["plugged-in","unplugged","backup-started","backup-completed","backup-failed","resync-started","resync-completed","resync-failed","conversation-upload-started","conversation-upload-completed","conversation-upload-failed","conversation-search","device-hash-saved","device-hash-matched","device-hash-not-matched","device-backup-deleted","case-export","conversation-added-to-case","conversation-removed-from-case","device-message-added-to-collection","device-message-removed-from-collection","comment-added-to-device-message","comment-removed-from-device-message","tag-added-to-device-message","tag-removed-from-device-message","device-backup-created","conversation-uploaded"]},"date":{"type":"string","description":"ISO date string"},"conversationNames":{"description":"Required if type is either conversation-upload-completed or conversation-upload-started","type":"array","items":{"type":"string"}},"appVersion":{"type":"string","description":"App version"},"organizationId":{"type":"string"},"deviceMetadata":{"type":"object"},"backupMetadata":{"type":"object"},"extractionCode":{"type":"string"},"caseId":{"type":"string"},"osDetails":{"$ref":"#/components/schemas/OsDetailsDto"},"addImmediate":{"type":"boolean"},"failedReason":{"type":"string"},"telemetryV2":{"type":"boolean","description":"Set by app builds that already send the structured (v2) telemetry event for this activity, so the legacy activity-log translation is skipped and the activity is not recorded twice."}},"required":["systemId","deviceId","deviceType","type","date","conversationNames","appVersion"]},"CommentEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"text":{"type":"string","example":"Relevant to the timeline"},"source":{"type":"string","nullable":true,"description":"Id of the commented item (its kind is sourceType)","example":"507f1f77bcf86cd799439011"},"sourceType":{"type":"string","enum":["DeviceMessage","DeviceConversation","UserEmailData","FinancialAccount","CaseFile","UserEmailDataThread","LinkedInConversation","LinkedInJobApplication","AiChatConversation"]},"deviceMessage":{"type":"string","nullable":true,"description":"Id of the commented message, for message comments","example":"507f1f77bcf86cd799439011"},"user":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserReferenceEntity"}]},"parent":{"type":"string","nullable":true,"description":"Id of the comment this one replies to","example":"507f1f77bcf86cd799439011"},"replyCount":{"type":"number","example":0}},"required":["createdAt","updatedAt","id","text","source","sourceType","deviceMessage","user","parent","replyCount"]},"CreateCommentDto":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]},"UpdateCommentDto":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]},"ReplyCommentDto":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]},"UserNotificationsEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"user":{"$ref":"#/components/schemas/UserEntity"},"createdBy":{"$ref":"#/components/schemas/UserEntity"},"conversation":{"$ref":"#/components/schemas/DeviceConversationEntity"},"invitedUserName":{"type":"string"},"invitedUserEmail":{"type":"string"},"isRead":{"type":"boolean"},"readAt":{"format":"date-time","type":"string"},"type":{"type":"string","enum":["default","imported-new-conversation","added-conversation-to-existing-case","replied-to-comment","mentioned-in-comment","added-to-case","added-to-organization","new-comment","oc-suggestion-created-or-updated","oc-suggestion-approved","oc-suggestion-rejected","oc-suggestion-message","oc-data-approved","oc-member-added","oc-member-removed"]},"text":{"type":"string"},"caseId":{"type":"string"},"caseName":{"type":"string"},"conversationId":{"type":"string"},"importedConversationId":{"type":"string"},"importedConversationName":{"type":"string"},"caseSlug":{"type":"string"},"messageId":{"type":"string"},"commentId":{"type":"string"},"replyCommentId":{"type":"string"},"commentText":{"type":"string"},"replyText":{"type":"string"},"messageIndex":{"type":"number"},"commentIndex":{"type":"number"},"replyCommentIndex":{"type":"number"}},"required":["createdAt","updatedAt","id","user","createdBy","conversation","invitedUserName","invitedUserEmail","isRead","readAt","type","text","caseId","caseName","conversationId","importedConversationId","importedConversationName","caseSlug","messageId","commentId","replyCommentId","commentText","replyText","messageIndex","commentIndex","replyCommentIndex"]},"DefaultPriceEntity":{"type":"object","properties":{"id":{"type":"string","example":"price_1PExample"},"active":{"type":"boolean","example":true},"billing_scheme":{"type":"string","example":"per_unit"},"created":{"type":"number","example":1736935500},"currency":{"type":"string","example":"usd"},"custom_unit_amount":{"type":"object","example":null,"nullable":true},"livemode":{"type":"boolean","example":false},"lookup_key":{"type":"string","example":"firm_monthly"},"metadata":{"type":"object","example":{}},"type":{"type":"string","example":"recurring"},"unit_amount":{"type":"number","example":24900},"unit_amount_decimal":{"type":"string","example":"24900"}},"required":["id","active","billing_scheme","created","currency","custom_unit_amount","livemode","lookup_key","metadata","type","unit_amount","unit_amount_decimal"]},"AvailablePlanEntity":{"type":"object","properties":{"id":{"type":"string","example":"prod_Example"},"active":{"type":"boolean","example":true},"attributes":{"example":[],"type":"array","items":{"type":"string"}},"created":{"type":"number","example":1736935500},"default_price":{"$ref":"#/components/schemas/DefaultPriceEntity"},"features":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"}}},"example":[{"name":"Unlimited seats"}]},"livemode":{"type":"boolean","example":false},"metadata":{"type":"object","example":{"plan":"firm"}},"name":{"type":"string","example":"Firm"},"tax_code":{"type":"object","example":null,"nullable":true},"unit_label":{"type":"object","example":null,"nullable":true},"url":{"type":"object","example":null,"nullable":true},"isPayAsYouGo":{"type":"boolean","example":false},"isCurrentPlan":{"type":"boolean","example":true}},"required":["id","active","attributes","created","default_price","features","livemode","metadata","name","tax_code","unit_label","url","isPayAsYouGo","isCurrentPlan"]},"InvoiceLineItemEntity":{"type":"object","properties":{"id":{"type":"string","example":"il_1PExample"},"amount":{"type":"number","example":24900},"amount_excluding_tax":{"type":"number","example":24900},"currency":{"type":"string","example":"usd"},"description":{"type":"string","example":"1 x Firm (at $249.00 / month)"},"discount_amounts":{"type":"array","items":{"type":"object"},"example":[]},"discountable":{"type":"boolean","example":true},"discounts":{"example":[],"type":"array","items":{"type":"string"}},"livemode":{"type":"boolean","example":false},"metadata":{"type":"object","example":{}},"period":{"type":"object","example":{"start":1736935500,"end":1739613900}},"product_lookup_description":{"type":"string","example":"Subscription"},"plan":{"$ref":"#/components/schemas/AvailablePlanEntity"},"price":{"$ref":"#/components/schemas/DefaultPriceEntity"},"quantity":{"type":"number","example":1},"unit_amount_excluding_tax":{"type":"number","example":24900}},"required":["id","amount","amount_excluding_tax","currency","description","discount_amounts","discountable","discounts","livemode","metadata","period","product_lookup_description","plan","price","quantity","unit_amount_excluding_tax"]},"UpcomingInvoiceEntity":{"type":"object","properties":{"account_country":{"type":"string","example":"US"},"account_name":{"type":"string","example":"Acme Legal"},"amount_due":{"type":"number","example":24900},"amount_paid":{"type":"number","example":0},"amount_remaining":{"type":"number","example":24900},"attempted":{"type":"boolean","example":false},"billing_reason":{"type":"string","example":"upcoming"},"collection_method":{"type":"string","example":"charge_automatically"},"created":{"type":"number","example":1736935500},"currency":{"type":"string","example":"usd"},"customer_address":{"type":"object","example":null,"nullable":true},"customer_email":{"type":"string","example":"billing@example.com"},"customer_name":{"type":"string","example":"Acme Legal"},"customer_phone":{"type":"string","example":null,"nullable":true},"description":{"type":"string","example":null,"nullable":true},"discount":{"type":"object","example":null,"nullable":true},"discounts":{"example":[],"type":"array","items":{"type":"string"}},"due_date":{"type":"object","example":null,"nullable":true},"livemode":{"type":"boolean","example":false},"period_end":{"type":"number","example":1739613900},"period_start":{"type":"number","example":1736935500},"status":{"type":"string","example":"draft"},"subtotal":{"type":"number","example":24900},"subtotal_excluding_tax":{"type":"number","example":24900},"tax":{"type":"number","example":0},"total":{"type":"number","example":24900},"total_discount_amounts":{"type":"array","items":{"type":"object"},"example":[]},"total_excluding_tax":{"type":"number","example":24900},"total_tax_amounts":{"type":"array","items":{"type":"object"},"example":[]},"lines":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceLineItemEntity"}}},"required":["account_country","account_name","amount_due","amount_paid","amount_remaining","attempted","billing_reason","collection_method","created","currency","customer_address","customer_email","customer_name","customer_phone","description","discount","discounts","due_date","livemode","period_end","period_start","status","subtotal","subtotal_excluding_tax","tax","total","total_discount_amounts","total_excluding_tax","total_tax_amounts","lines"]},"BillingAlertEntity":{"type":"object","properties":{"pastDue":{"type":"boolean","description":"Subscription is past due (in dunning)","example":true},"graceUntil":{"format":"date-time","type":"string","description":"End of the grace window while past due","nullable":true,"example":"2026-01-22T10:15:00.000Z"},"blocked":{"type":"boolean","description":"Access is blocked (grace expired, or subscription unpaid/canceled)","example":false}},"required":["pastDue","graceUntil","blocked"]},"AvailablePlansListEntity":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AvailablePlanEntity"}},"attachedPaymentMethod":{"type":"object","description":"Attached payment method","example":{"id":"pm_1PExample","type":"card","card":{"brand":"visa","last4":"4242","exp_month":4,"exp_year":2030}}},"upcomingInvoice":{"description":"Attached payment method","allOf":[{"$ref":"#/components/schemas/UpcomingInvoiceEntity"}]},"currentPlan":{"$ref":"#/components/schemas/AvailablePlanEntity"},"billingAlert":{"nullable":true,"description":"Dunning banner state; null when billing is healthy","type":"object","allOf":[{"$ref":"#/components/schemas/BillingAlertEntity"}]}},"required":["data","attachedPaymentMethod","upcomingInvoice","currentPlan","billingAlert"]},"CouponCodeDetailsResponseEntity":{"type":"object","properties":{"isValid":{"type":"boolean","example":true},"id":{"type":"string","example":"promo_1PExample"},"type":{"type":"string","enum":["percentage","flat"],"example":"percentage"},"value":{"type":"number","example":20}},"required":["isValid","id","type","value"]},"CheckCouponValidityDto":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]},"CheckoutSessionResponseEntity":{"type":"object","properties":{"url":{"type":"string","example":"https://checkout.stripe.com/c/pay/cs_test_a1Example"}},"required":["url"]},"CreateCheckoutSessionDto":{"type":"object","properties":{"priceId":{"type":"string"},"cancelUrl":{"type":"string"},"successUrl":{"type":"string"},"couponId":{"type":"string"}},"required":["priceId","cancelUrl","successUrl","couponId"]},"StripeCustomerSessionEntity":{"type":"object","properties":{"clientSecret":{"type":"string","example":"cuss_secret_Example"},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"},"expiresAt":{"format":"date-time","type":"string","example":"2026-01-15T10:45:00.000Z"}},"required":["clientSecret","createdAt","expiresAt"]},"BillingPortalResponseEntity":{"type":"object","properties":{"url":{"type":"string","example":"https://billing.stripe.com/p/session/test_Example"},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"},"customer":{"type":"string","example":"cus_Example"}},"required":["url","createdAt","customer"]},"CreateBillingPortalSessionDto":{"type":"object","properties":{"type":{"type":"string","enum":["payment_method_update","subscription_cancel","subscription_update"],"default":"payment_method_update"},"returnUrl":{"type":"string"}},"required":["returnUrl"]},"CreateUpdateAppConfigDto":{"type":"object","properties":{"systemId":{"type":"string","maxLength":99999,"minLength":1},"defaultBackupPath":{"type":"string"},"lastUsedBackupPath":{"type":"string"},"otherSettings":{"type":"object"},"osDetails":{"$ref":"#/components/schemas/OsDetailsDto"},"lastUsedAppVersion":{"type":"string"},"lastAppUsedAt":{"type":"string"}},"required":["systemId"]},"AddUpdateAppConfigDeviceSettingsDto":{"type":"object","properties":{"systemId":{"type":"string","maxLength":99999,"minLength":1},"deviceId":{"type":"string","maxLength":99999,"minLength":1},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"defaultBackupPath":{"type":"string"},"lastUsedBackupPath":{"type":"string"},"autoBackupFrequency":{"type":"string","enum":["none","daily","weekly","monthly"]},"deviceMetadata":{"type":"object"},"otherSettings":{"type":"object"},"lastBackupAt":{"format":"date-time","type":"string"},"lastLocalBackupAt":{"format":"date-time","type":"string"}},"required":["systemId","deviceId","deviceType"]},"DeleteAppConfigDeviceSettingsDto":{"type":"object","properties":{"systemId":{"type":"string","maxLength":99999,"minLength":1},"deviceId":{"type":"string","maxLength":99999,"minLength":1},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]}},"required":["systemId","deviceId","deviceType"]},"AddGoodFactCaseDto":{"type":"object","properties":{"lawyerEmail":{"type":"string","description":"Lawyer Email"},"organizationName":{"type":"string","description":"Organization Name"},"goodfactCaseName":{"type":"string","description":"Goodfact Case Name"},"goodfactEndpoint":{"type":"string","description":"Integration Endpoint"},"caseAPIKey":{"type":"string","description":"Goodfact Case Integration API Key"}},"required":["lawyerEmail","organizationName","goodfactCaseName","goodfactEndpoint","caseAPIKey"]},"LegalMatterTypeEntity":{"type":"object","properties":{"id":{"type":"string","example":"adoption"},"name":{"type":"string","example":"Adoption"}},"required":["id","name"]},"LegalMatterSummaryEntity":{"type":"object","properties":{"id":{"type":"string","example":"12345","description":"Provider-assigned matter id (integer string for Clio, UUID for Smokeball)."},"displayNumber":{"type":"string","example":"00123-Smith"},"name":{"type":"string","example":"Smith v. Acme Corp"},"description":{"type":"string","example":"Wrongful termination claim"},"status":{"type":"string","enum":["open","pending","closed"],"example":"open"},"matterType":{"description":"Smokeball matter type; not populated for Clio.","allOf":[{"$ref":"#/components/schemas/LegalMatterTypeEntity"}]},"isLinked":{"type":"boolean","example":true,"description":"True when this matter is already linked to a Hearsay case."},"linkedCaseId":{"type":"string","example":"64f0c2a1e3b9a40012ab34cd","description":"The linked Hearsay case id, present only when `isLinked` is true."}},"required":["id","name","status","isLinked"]},"LegalMattersMetaEntity":{"type":"object","properties":{"page":{"type":"number","example":1,"description":"1-based page number."},"limit":{"type":"number","example":25,"description":"Items per page."},"hasMore":{"type":"boolean","example":true,"description":"True when more pages are available after this one."}},"required":["page","limit","hasMore"]},"LegalMattersResponseEntity":{"type":"object","properties":{"matters":{"type":"array","items":{"$ref":"#/components/schemas/LegalMatterSummaryEntity"}},"meta":{"$ref":"#/components/schemas/LegalMattersMetaEntity"}},"required":["matters","meta"]},"LinkMatterDto":{"type":"object","properties":{"caseId":{"type":"string","description":"CaseId for the case to link"}},"required":["caseId"]},"AutoCollectionRoleDto":{"type":"object","properties":{"role":{"type":"string","description":"Collection role, e.g. \"Ex-Spouse\""},"hint":{"type":"string"},"required":{"type":"boolean"},"channels":{"description":"Delivery channels, e.g. [\"email\", \"slack_teams\"]","type":"array","items":{"type":"string"}}},"required":["role","required","channels"]},"AutoCollectionDateRangeDto":{"type":"object","properties":{"start":{"format":"date-time","type":"string","nullable":true},"end":{"format":"date-time","type":"string","nullable":true}}},"AutoCollectionTemplateDto":{"type":"object","properties":{"caseType":{"type":"string","description":"Front case-type vocabulary (e.g. \"employment\")"},"sources":{"type":"array","description":"Pre-mapped MasterDataSourceName values","items":{"type":"string","enum":["ios","android","facebook_dump","instagram_dump","threads_dump","gmail","yahoo","outlook","icloud","imap","ews","files","bank-docs","linkedin_dump","claude_dump","chatgpt_dump","gemini_dump"]}},"roles":{"type":"array","items":{"$ref":"#/components/schemas/AutoCollectionRoleDto"}},"documentRequests":{"type":"array","items":{"type":"string"}},"dateRange":{"$ref":"#/components/schemas/AutoCollectionDateRangeDto"}},"required":["caseType","sources","roles","documentRequests"]},"AutoCollectionSettingsDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Pre-configure the auto-created case + attach the client as custodian (fires the collection email). Requires autoCreateFromWebhook."},"template":{"$ref":"#/components/schemas/AutoCollectionTemplateDto"}}},"UpdateLegalCasesSettingsDto":{"type":"object","properties":{"autoCreateFromWebhook":{"type":"boolean","description":"Auto-create a Hearsay case when a matter is created in Clio"},"autoCollection":{"$ref":"#/components/schemas/AutoCollectionSettingsDto"}}},"UpdateLegalDocumentsSettingsDto":{"type":"object","properties":{"autoSync":{"type":"boolean","description":"Enable automatic document sync from Clio"}}},"UpdateDataSourceSettingsDto":{"type":"object","properties":{"enabled":{"type":"boolean"}}},"UpdateEmailDataSourceSettingsDto":{"type":"object","properties":{"enabled":{"type":"boolean"},"format":{"type":"string","enum":["pdf","eml"]}}},"UpdateDeviceDataSourceSettingsDto":{"type":"object","properties":{"enabled":{"type":"boolean"},"format":{"type":"string","enum":["pdf","rsf"]}}},"FinancialExportTypesDto":{"type":"object","properties":{"transactions":{"type":"boolean","description":"Export bank transactions"},"recurringTransactions":{"type":"boolean","description":"Export recurring transactions"},"holdings":{"type":"boolean","description":"Export holdings"},"liabilities":{"type":"boolean","description":"Export liabilities"},"investmentTransactions":{"type":"boolean","description":"Export investment transactions"},"statements":{"type":"boolean","description":"Export account statements as a ZIP bundle (out-of-band). Drives the default (no-filter) export; an explicit financial filter with a statements-bundle item overrides this toggle."}}},"UpdateFinancialDataSourceSettingsDto":{"type":"object","properties":{"enabled":{"type":"boolean"},"format":{"type":"string","enum":["pdf","csv"]},"types":{"$ref":"#/components/schemas/FinancialExportTypesDto"}}},"DataSourcesSettingsDto":{"type":"object","properties":{"file":{"$ref":"#/components/schemas/UpdateDataSourceSettingsDto"},"email":{"$ref":"#/components/schemas/UpdateEmailDataSourceSettingsDto"},"device":{"$ref":"#/components/schemas/UpdateDeviceDataSourceSettingsDto"},"financial":{"$ref":"#/components/schemas/UpdateFinancialDataSourceSettingsDto"},"reddit":{"$ref":"#/components/schemas/UpdateDataSourceSettingsDto"},"discord":{"$ref":"#/components/schemas/UpdateDataSourceSettingsDto"}}},"UpdateLegalSettingsDto":{"type":"object","properties":{"cases":{"$ref":"#/components/schemas/UpdateLegalCasesSettingsDto"},"documents":{"$ref":"#/components/schemas/UpdateLegalDocumentsSettingsDto"},"dataSources":{"$ref":"#/components/schemas/DataSourcesSettingsDto"}}},"ExportToProviderResultEntity":{"type":"object","properties":{"queued":{"type":"boolean","description":"False when nothing matched the request — no job was enqueued.","example":true},"jobId":{"type":"string","nullable":true,"description":"Bull job id. One in-flight export per (case, provider): a second request while the first is still running collapses onto it.","example":"legal-export-65a1b2c3d4e5f6a7b8c9d0e2-clio"},"matched":{"type":"object","description":"Records matched per source type. A source the caller named explicitly is rejected with 409 when it matches nothing, so a zero here only ever appears for an unscoped request.","example":{"Device":3,"UserEmailDataProvider":1}},"totalMatched":{"type":"number","example":4}},"required":["queued","matched","totalMatched"]},"ExportAccountTypesFilterDto":{"type":"object","properties":{"include":{"type":"array","items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}},"exclude":{"type":"array","items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}}}},"FinancialExportItemDto":{"type":"object","properties":{"exportType":{"type":"string","enum":["transactions","holdings","liabilities","recurring-transactions","investment-transactions","statements-bundle"]},"accountTypes":{"$ref":"#/components/schemas/ExportAccountTypesFilterDto"},"startDate":{"type":"string"},"endDate":{"type":"string"},"category":{"type":"string"},"transactionType":{"type":"string","enum":["debit","credit"]},"minAmount":{"type":"number"},"maxAmount":{"type":"number"},"type":{"type":"string"},"frequency":{"type":"string"},"isActive":{"type":"boolean"},"liabilityType":{"type":"string","enum":["credit","mortgage","student"]},"isOverdue":{"type":"boolean"},"tags":{"type":"array","items":{"type":"array"}}},"required":["exportType"]},"ExportSourceFiltersDto":{"type":"object","properties":{"conversations":{"type":"array","items":{"type":"array"}},"excludeConversations":{"type":"array","items":{"type":"array"}},"startDate":{"type":"string"},"endDate":{"type":"string"},"tags":{"type":"array","items":{"type":"array"}},"threads":{"description":"Specific email threads to export. Omit to export every thread under the extraction code (minus `excludeThreads`).","type":"array","items":{"type":"array"}},"excludeThreads":{"type":"array","items":{"type":"array"}},"includeHidden":{"type":"boolean","default":false,"description":"When true, threads the user hid in the platform (hiddenAt != null) are exported too. Default false, matching the download export. Ignored when `threads` is set — an explicit id selection is honored regardless of hidden state."},"participants":{"description":"Correspondent emails (to/from) — matches thread participants","type":"array","items":{"type":"array"}},"sourceEmail":{"type":"string"},"folders":{"type":"array","items":{"type":"array"}},"searchText":{"type":"string"},"receivedAfter":{"type":"string"},"receivedBefore":{"type":"string"},"financial":{"type":"array","items":{"$ref":"#/components/schemas/FinancialExportItemDto"}}}},"ExportRenderOptionsDto":{"type":"object","properties":{"exportConfig":{"type":"array","items":{"type":"string","enum":["phone-numbers","comments","tags","app-icons","device-name","images","hide-attachment-links","extra-texts","line-break","device-logs","custom-entry","recently-deleted","deleted-messages-inline","deleted-messages-page","deleted-messages-inline-page","ignore-conversations","search-history-page","hide-page-numbers","print-footer-work-timestamp"]}},"sortBy":{"type":"string","enum":["old-to-new","new-to-old"]},"chunkType":{"type":"string","enum":["none","day","week","month","year"]},"timezone":{"type":"string","description":"IANA timezone (e.g. \"America/New_York\") for timestamps"},"tagsPadding":{"type":"number","minimum":0,"maximum":20},"includeAttachments":{"type":"boolean","description":"Email only. Bundle each email's attachments alongside the rendered output instead of linking back to the platform. Changes the delivered artifact to a ZIP; subject to the files-zip size limits."}}},"ExportSourceDto":{"type":"object","properties":{"sourceType":{"type":"string","enum":["Device","UserEmailDataProvider","CaseFileProvider","FinancialAccountProvider","LegalDocumentProvider","DiscordUserProvider","RedditUserProvider","LinkedInUserProvider","AiChatProvider"]},"format":{"type":"string","enum":["pdf","csv","rsmf","eml"]},"filters":{"$ref":"#/components/schemas/ExportSourceFiltersDto"},"options":{"$ref":"#/components/schemas/ExportRenderOptionsDto"}}},"ExportToProviderDto":{"type":"object","properties":{"provider":{"type":"string","enum":["clio","smokeball","dropbox"],"default":"clio"},"includeFailed":{"type":"boolean","default":false},"includeSkipped":{"type":"boolean","default":false,"description":"Also export records previously skipped because the source was not enabled / the case was not linked when the data arrived. Requires `sources` to be set — see SkippedRequiresExplicitSources."},"extractionCodeIds":{"description":"Custodian (extraction code) scope — orthogonal to filters.","type":"array","items":{"type":"array"}},"sources":{"description":"Per-source selection + filters. OMIT to export every pending source with no filters. Each entry only accepts the filter keys valid for its sourceType (others are rejected) — see the example.","example":[{"sourceType":"Device","format":"pdf","filters":{"conversations":["665f0a1b2c3d4e5f00000001"],"startDate":"2021-01-01","endDate":"2021-12-31","tags":["665f0a1b2c3d4e5f00000002"]}},{"sourceType":"UserEmailDataProvider","filters":{"participants":["jane@example.com"],"receivedAfter":"2021-01-01","receivedBefore":"2021-12-31"}},{"sourceType":"FinancialAccountProvider","filters":{"financial":[{"exportType":"transactions","startDate":"2021-01-01","endDate":"2021-12-31","category":"Travel","transactionType":"debit","accountTypes":{"include":["depository"]}},{"exportType":"statements-bundle","startDate":"2021-01-01","endDate":"2021-06-30"}]}}],"type":"array","items":{"$ref":"#/components/schemas/ExportSourceDto"}}}},"LegalSyncRecordEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f3"},"destination":{"type":"string","enum":["clio","smokeball","dropbox"],"example":"clio"},"integrationType":{"type":"string","enum":["goodfact","clio","smokeball"],"nullable":true,"deprecated":true,"description":"Deprecated alias of `destination`, present only for CMP destinations. Null for storage destinations, which have no integration type. Use `destination`.","example":"clio"},"sourceType":{"type":"string","example":"CaseFileProvider"},"status":{"type":"string","enum":["pending","processing","completed","failed","disabled","skipped"],"example":"completed"},"fileName":{"type":"string","nullable":true,"example":"text-messages-export.pdf"},"target":{"type":"string","nullable":true,"description":"Where the document was filed: a matter id for a CMP, a folder path for storage.","example":"12345"},"matterId":{"type":"string","nullable":true,"deprecated":true,"description":"Deprecated alias of `target`. Use `target`.","example":"12345"},"errorMessage":{"type":"string","nullable":true,"example":null},"requiresExport":{"type":"boolean","example":false},"remoteDocumentId":{"type":"string","nullable":true,"description":"Destination document id once uploaded (null until sent).","example":"778899"},"startedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:15:00.000Z"},"completedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:20:00.000Z"},"failedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"createdAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:15:00.000Z"},"updatedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:20:00.000Z"}},"required":["id","destination","sourceType","status","requiresExport"]},"CreateLegalExpenseDto":{"type":"object","properties":{"amount":{"type":"number","example":49,"description":"Expense amount in the matter currency."},"description":{"type":"string","example":"Hearsay: phone extraction upload","description":"Description shown on the provider expense line item."},"date":{"type":"string","example":"2026-05-29","description":"ISO-8601 date the expense was incurred. Defaults to today when omitted."},"expenseCategoryId":{"type":"string","description":"Optional provider expense-category / activity-code id used to categorize the expense."}},"required":["amount","description"]},"SetCaseDestinationFolderDto":{"type":"object","properties":{"path":{"type":"string","example":"/Clients/Smith v. Acme","description":"Absolute folder path inside the connected account, as returned by the folder listing."}},"required":["path"]},"DataClioWebhook":{"type":"object","properties":{"webhook_id":{"type":"number","description":"Webhook ID"},"id":{"type":"number","description":"Record id"},"etag":{"type":"string","description":"Etag of the record"}},"required":["webhook_id","id","etag"]},"MetaClioWebhook":{"type":"object","properties":{"event":{"type":"string","description":"Event type (created, updated, deleted)"},"model":{"type":"string","description":"Model name (matter, document, etc)"},"webhook_id":{"type":"number","description":"Webhook ID"}},"required":["event","webhook_id"]},"ClioMatterWebHookDto":{"type":"object","properties":{"data":{"description":"Object with the data","allOf":[{"$ref":"#/components/schemas/DataClioWebhook"}]},"meta":{"description":"Metadata of the webhook event","allOf":[{"$ref":"#/components/schemas/MetaClioWebhook"}]}},"required":["data","meta"]},"ClioDocumentWebhookDto":{"type":"object","properties":{"data":{"description":"Object with the data","allOf":[{"$ref":"#/components/schemas/DataClioWebhook"}]},"meta":{"description":"Metadata of the webhook event","allOf":[{"$ref":"#/components/schemas/MetaClioWebhook"}]}},"required":["data","meta"]},"SmokeballWebhookDto":{"type":"object","properties":{"type":{"type":"string","description":"Event type, e.g. 'matter.created'"},"subscriptionId":{"type":"string","description":"Subscription that produced this delivery"},"accountId":{"type":"string","description":"Smokeball firm/account id"},"userId":{"type":"string","description":"Smokeball user that triggered the event"},"source":{"type":"string","description":"Origin of the change"},"payload":{"type":"object","description":"Event-specific payload (ids of the changed resource)"},"timestamp":{"type":"string","description":"Event timestamp (provider-supplied)"}},"required":["type","subscriptionId","accountId"]},"CaseEventUserEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane.doe@example.com"}},"required":["createdAt","updatedAt","id","name","email"]},"CaseEventEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"type":{"type":"string","enum":["financial_connection_removed","oc_invited","oc_accepted","oc_member_added","oc_member_removed","oc_ec_created","oc_suggestion_created","oc_suggestion_updated","oc_suggestion_message","oc_suggestion_rejected","oc_ec_approved","oc_data_approved","oc_data_revoked","oc_code_released","oc_code_unreleased","member_invited","member_accepted","case_created","case_status_changed","extraction_code_created","extraction_code_updated","extraction_code_locked","custodian_updated","device_attached","data_uploaded","export_started","export_finished","export_errored","export_cancelled"]},"category":{"type":"string","enum":["general","case_data","oc"]},"description":{"type":"string","example":"Export finished"},"createdBy":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/CaseEventUserEntity"}]},"metadata":{"type":"object","example":{}}},"required":["createdAt","updatedAt","id","type","category","description","createdBy","metadata"]},"CreateCaseForUserDto":{"type":"object","properties":{"custodianEmail":{"type":"string","maxLength":100,"minLength":3},"custodianName":{"type":"string","maxLength":100,"minLength":3},"custodianPhone":{"type":"string","maxLength":50,"minLength":3},"sendEmail":{"type":"boolean"},"label":{"type":"string","maxLength":100,"minLength":3},"allowShareAll":{"type":"boolean","deprecated":true,"description":"Use config instead"},"config":{"$ref":"#/components/schemas/ExtractionCodeConfigDto"},"requestedSources":{"type":"array","items":{"type":"string"}},"dueDate":{"type":"string"},"sendReminders":{"$ref":"#/components/schemas/ExtractionCodeSendRemindersDto"},"metadata":{"type":"object"},"watchlist":{"type":"boolean","default":false},"ocRequired":{"type":"boolean","default":true,"description":"OC cases only: when false, the OL creates/keeps this code without the opposing-council approval gate (code stays `na`, immediately usable, and the custodian email is sent). Ignored on non-OC cases. Defaults to true (gate on)."},"paymentRequired":{"type":"boolean","description":"Whether payment is required for this extraction code"},"paymentAmount":{"type":"number","description":"Payment amount in cents"},"paymentCurrency":{"type":"string","description":"Payment currency (defaults to usd)","default":"usd"},"paymentSuccessRedirectUrl":{"type":"string","description":"Payment success redirect URL"},"productName":{"type":"string","description":"Product name"},"prepaidItems":{"description":"Prepaid basket. Each entry becomes its own Stripe line item and grants units on payment. Prices are negotiable — unitAmountCents is what the customer is charged, and is never validated against the catalog default.","type":"array","items":{"$ref":"#/components/schemas/PrepaidItemDto"}},"name":{"type":"string","maxLength":100,"minLength":3},"description":{"type":"string"},"matterId":{"type":"string","maxLength":100,"description":"The firm's own client/matter reference. Printed on Stripe invoice line items and carried in invoice metadata."},"tags":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["open","closed","archived","pending"],"default":"pending"},"priority":{"type":"string","enum":["low","medium","high","urgent"],"default":"low"},"isDefault":{"type":"boolean","default":false},"createExtractionCode":{"type":"boolean","default":false},"addExistingUsersToCase":{"type":"boolean","default":false},"addExistingUsersAccessLevel":{"type":"string","enum":["owner","admin","commenter","viewer"],"default":"admin"},"users":{"type":"array","items":{"$ref":"#/components/schemas/InviteCaseUser"}},"userId":{"type":"string","description":"required if userEmail not sent"},"userEmail":{"type":"string","description":"required if userId not sent"},"organization":{"type":"string"}},"required":["dueDate","name","organization"]},"DeleteCaseConversationsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"required if caseSlug not sent"},"caseSlug":{"type":"string","description":"required if caseId not sent"},"orgId":{"type":"string","description":"required if orgSlug not sent"},"orgSlug":{"type":"string","description":"required if orgId not sent"},"deleteCaseIfNoConversationsLeft":{"type":"boolean","default":false}}},"ExtractionCodeCreateSingleResponseEntity":{"type":"object","properties":{"caseId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"code":{"type":"string","example":"K7Q2MZ"}},"required":["caseId","code"]},"CreateExtractionCodeResponseEntity":{"type":"object","properties":{"data":{"example":[{"caseId":"65a1b2c3d4e5f6a7b8c9d0e2","code":"K7Q2MZ"}],"type":"array","items":{"$ref":"#/components/schemas/ExtractionCodeCreateSingleResponseEntity"}}},"required":["data"]},"SingleExtractionCodeCreateDto":{"type":"object","properties":{"custodianEmail":{"type":"string","maxLength":100,"minLength":3},"custodianName":{"type":"string","maxLength":100,"minLength":3},"custodianPhone":{"type":"string","maxLength":50,"minLength":3},"sendEmail":{"type":"boolean"},"label":{"type":"string","maxLength":100,"minLength":3},"allowShareAll":{"type":"boolean","deprecated":true,"description":"Use config instead"},"config":{"$ref":"#/components/schemas/ExtractionCodeConfigDto"},"requestedSources":{"type":"array","items":{"type":"string"}},"dueDate":{"type":"string"},"sendReminders":{"$ref":"#/components/schemas/ExtractionCodeSendRemindersDto"},"metadata":{"type":"object"},"watchlist":{"type":"boolean","default":false},"ocRequired":{"type":"boolean","default":true,"description":"OC cases only: when false, the OL creates/keeps this code without the opposing-council approval gate (code stays `na`, immediately usable, and the custodian email is sent). Ignored on non-OC cases. Defaults to true (gate on)."},"paymentRequired":{"type":"boolean","description":"Whether payment is required for this extraction code"},"paymentAmount":{"type":"number","description":"Payment amount in cents"},"paymentCurrency":{"type":"string","description":"Payment currency (defaults to usd)","default":"usd"},"paymentSuccessRedirectUrl":{"type":"string","description":"Payment success redirect URL"},"productName":{"type":"string","description":"Product name"},"prepaidItems":{"description":"Prepaid basket. Each entry becomes its own Stripe line item and grants units on payment. Prices are negotiable — unitAmountCents is what the customer is charged, and is never validated against the catalog default.","type":"array","items":{"$ref":"#/components/schemas/PrepaidItemDto"}},"caseId":{"type":"string"}},"required":["dueDate","caseId"]},"CreateMultipleExtractionCodeDto":{"type":"object","properties":{"ecCodeData":{"type":"array","items":{"$ref":"#/components/schemas/SingleExtractionCodeCreateDto"}}},"required":["ecCodeData"]},"UpdateExtractionCodeAdminDto":{"type":"object","properties":{"custodianEmail":{"type":"string","maxLength":100,"minLength":3},"custodianName":{"type":"string","maxLength":100,"minLength":3},"custodianPhone":{"type":"string","maxLength":50,"minLength":3},"sendEmail":{"type":"boolean"},"label":{"type":"string","maxLength":100,"minLength":3},"allowShareAll":{"type":"boolean","deprecated":true,"description":"Use config instead"},"config":{"$ref":"#/components/schemas/ExtractionCodeConfigDto"},"requestedSources":{"type":"array","items":{"type":"string"}},"dueDate":{"type":"string"},"sendReminders":{"$ref":"#/components/schemas/ExtractionCodeSendRemindersDto"},"metadata":{"type":"object"},"watchlist":{"type":"boolean","default":false},"ocRequired":{"type":"boolean","default":true,"description":"OC cases only: when false, the OL creates/keeps this code without the opposing-council approval gate (code stays `na`, immediately usable, and the custodian email is sent). Ignored on non-OC cases. Defaults to true (gate on)."},"paymentRequired":{"type":"boolean","description":"Whether payment is required for this extraction code"},"paymentAmount":{"type":"number","description":"Payment amount in cents"},"paymentCurrency":{"type":"string","description":"Payment currency (defaults to usd)","default":"usd"},"paymentSuccessRedirectUrl":{"type":"string","description":"Payment success redirect URL"},"productName":{"type":"string","description":"Product name"},"prepaidItems":{"description":"Prepaid basket. Each entry becomes its own Stripe line item and grants units on payment. Prices are negotiable — unitAmountCents is what the customer is charged, and is never validated against the catalog default.","type":"array","items":{"$ref":"#/components/schemas/PrepaidItemDto"}},"caseId":{"type":"string"},"extractionCodeId":{"type":"string","description":"required if code is not provided"},"code":{"type":"string","description":"required if extractionCodeId is not provided"}},"required":["dueDate","caseId"]},"CreateBulkExtractionCodeDto":{"type":"object","properties":{"createIfEvenExists":{"type":"boolean"}}},"RerunExportDto":{"type":"object","properties":{"exportId":{"type":"string"}},"required":["exportId"]},"RerunExportsResponseEntity":{"type":"object","properties":{"batchId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f2"},"totalCount":{"type":"number","example":3},"invalidCount":{"type":"number","example":0}},"required":["batchId","totalCount","invalidCount"]},"RerunExportsDto":{"type":"object","properties":{"exportIds":{"type":"array","items":{"type":"string"}}},"required":["exportIds"]},"CaseExportRerunBatchItemEntity":{"type":"object","properties":{"export":{"type":"string"},"status":{"type":"string","enum":["pending","running","completed","failed","timeout","cancelled","invalid"]},"startedAt":{"format":"date-time","type":"string","nullable":true},"finishedAt":{"format":"date-time","type":"string","nullable":true},"errorMessage":{"type":"string","nullable":true}},"required":["export","status"]},"CaseExportRerunBatchEntity":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["queued","in-progress","completed"]},"totalCount":{"type":"number"},"successCount":{"type":"number"},"failureCount":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/CaseExportRerunBatchItemEntity"}},"startedAt":{"format":"date-time","type":"string","nullable":true},"completedAt":{"format":"date-time","type":"string","nullable":true}},"required":["id","status","totalCount","successCount","failureCount","items"]},"FetchMessagesCountResponseEntity":{"type":"object","properties":{"messagesCount":{"type":"number","description":"The number of messages","example":100},"conversationsCount":{"type":"number","description":"The number of conversations","example":100},"exportMessagesCount":{"type":"number","description":"The number of conversations","example":100}},"required":["messagesCount","conversationsCount","exportMessagesCount"]},"MoveCaseToOrgDto":{"type":"object","properties":{"caseId":{"type":"string","description":"The case ID to move"},"targetOrgId":{"type":"string","description":"The target organization ID"},"emails":{"description":"The emails to move","type":"array","items":{"type":"string"}}},"required":["caseId","targetOrgId","emails"]},"MoveDeviceToCaseDto":{"type":"object","properties":{"deviceId":{"type":"string","description":"The device ID to move"},"sourceCaseId":{"type":"string","description":"The source case ID (where the device currently is)"},"targetCaseId":{"type":"string","description":"The target case ID (where to move the device)"},"dryRun":{"type":"boolean","description":"If true, validate and report what would change without writing","default":false}},"required":["deviceId","sourceCaseId","targetCaseId"]},"AdminDeleteDiscordProfileDto":{"type":"object","properties":{"discordProviderId":{"type":"string","description":"DiscordUserProvider ID"},"forceDelete":{"type":"boolean","description":"If true, permanently deletes all data. If false/omitted, soft-deletes (archives)."}},"required":["discordProviderId"]},"AdminDeleteRedditProfileDto":{"type":"object","properties":{"redditProviderId":{"type":"string","description":"RedditUserProvider ID"},"forceDelete":{"type":"boolean","description":"If true, permanently deletes all data. If false/omitted, soft-deletes (archives)."}},"required":["redditProviderId"]},"OrganizationDirectoryUserEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e4"},"name":{"type":"string","example":"Jane Doe"},"email":{"type":"string","example":"jane.doe@example.com"},"role":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"],"example":"admin"},"isActive":{"type":"boolean","example":true}},"required":["id","name","email","role","isActive"]},"OrganizationDirectoryEntryEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"Example Firm"},"slug":{"type":"string","example":"example-firm"},"isActive":{"type":"boolean","example":true},"users":{"description":"Members of this organization, excluding claimable placeholder accounts and memberships whose user no longer exists. An empty array therefore means \"no listable members\", not necessarily \"no memberships\".","type":"array","items":{"$ref":"#/components/schemas/OrganizationDirectoryUserEntity"}},"membershipCount":{"type":"number","description":"Total memberships for this organization, counted before truncation and before claimable/deleted users are filtered out. Always >= users.length.","example":12},"hasMoreUsers":{"type":"boolean","description":"True when this organization has more memberships than the response cap and `users` was truncated. For the full member list use GET /admin/user?organizationId=<id>&skip=0&limit=100 — page it with skip/limit rather than page/perPage, and note that endpoint does not exclude claimable accounts.","example":false}},"required":["createdAt","updatedAt","id","name","slug","isActive","users","membershipCount","hasMoreUsers"]},"OrganizationStorageStatsItemEntity":{"type":"object","properties":{"organizationName":{"type":"string","example":"Example Firm"},"organizationId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"totalUsagesInBytes":{"type":"number","example":104857600},"cases":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationStorageStatsCaseEntity"}}},"required":["organizationName","organizationId","totalUsagesInBytes","cases"]},"AddAllUsersToAllOrgCasesDto":{"type":"object","properties":{"orgId":{"type":"string"},"caseAccessLevel":{"type":"string","enum":["owner","admin","commenter","viewer"],"default":"commenter"}},"required":["orgId","caseAccessLevel"]},"AddUserToAllOrgCasesDto":{"type":"object","properties":{"orgId":{"type":"string"},"caseAccessLevel":{"type":"string","enum":["owner","admin","commenter","viewer"],"default":"commenter"},"email":{"type":"string","description":"Email of an existing organization member"}},"required":["orgId","caseAccessLevel","email"]},"AddNewUserToOrgDto":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string"},"invitedUserRole":{"type":"string","enum":["user","client","employee","billing","full_access","admin","owner","member"],"default":"member"},"orgId":{"type":"string","description":"required if orgName not sent"},"orgName":{"type":"string","description":"required if orgId not sent"},"password":{"type":"string","description":"If not set a random password is set"},"sendResetPasswordEmail":{"type":"boolean","default":true}},"required":["email","name"]},"RemoveUserFromOrgDto":{"type":"object","properties":{"email":{"type":"string","description":"required if userId not sent"},"userId":{"type":"string","description":"required if email not sent"},"orgId":{"type":"string","description":"required if orgName not sent"},"orgName":{"type":"string","description":"required if orgId not sent"},"deleteUser":{"type":"boolean","default":false}}},"TransferOrgOwnershipDto":{"type":"object","properties":{"currentOwnerEmail":{"type":"string","description":"Email of the current owner"},"newOwnerEmail":{"type":"string","description":"Email of the new owner"}},"required":["currentOwnerEmail","newOwnerEmail"]},"UpdateAdminOrganizationDto":{"type":"object","properties":{"name":{"type":"string"},"website":{"type":"string"},"logo":{"type":"string"},"contact":{"type":"string"},"addressLine1":{"type":"string"},"addressLine2":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"country":{"type":"string"},"zipCode":{"type":"string"},"email":{"type":"string"},"description":{"type":"string"},"phone":{"type":"array","items":{"type":"string","maximum":20,"minimum":1}},"metadata":{"type":"object"},"version":{"type":"string","enum":["v1","v2","eu","ca"]}}},"SetCacheValueDto":{"type":"object","properties":{"value":{"type":"object"},"ttl":{"type":"number"}},"required":["value"]},"RegisterResponseEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"accessToken":{"type":"string","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NWExYjJjMyJ9.c2lnbmF0dXJl"},"userProfile":{"$ref":"#/components/schemas/UserEntity"},"organizationInfo":{"$ref":"#/components/schemas/OrganizationDetailsEntity"}},"required":["createdAt","updatedAt","id","accessToken","userProfile"]},"AddUserDto":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"},"name":{"type":"string","maxLength":200,"minLength":3},"organizationName":{"type":"string"},"inviteEmails":{"type":"array","items":{"type":"array"}},"phone":{"type":"array","items":{"type":"string","maximum":20,"minimum":1}},"userType":{"type":"string"},"termsAccepted":{"type":"boolean"},"emailVerified":{"type":"boolean"},"claimable":{"type":"boolean"}},"required":["email","password","name","userType"]},"SetUserPasswordDto":{"type":"object","properties":{"email":{"type":"string","description":"required if userId not sent"},"userId":{"type":"string","description":"required if email not sent"},"password":{"type":"string"}},"required":["password"]},"SendPasswordResetEmailDto":{"type":"object","properties":{"email":{"type":"string","description":"required if userId not sent"},"userId":{"type":"string","description":"required if email not sent"}}},"LoginAsDto":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]},"DeleteUserAccountDto":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]},"UpdateEmailDto":{"type":"object","properties":{"oldEmail":{"type":"string"},"newEmail":{"type":"string"}},"required":["oldEmail","newEmail"]},"LockUserByEmailDto":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]},"UnlockUserByEmailDto":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]},"FetchDeviceUploadReportDto":{"type":"object","properties":{"startDate":{"type":"string"},"endDate":{"type":"string"}}},"StatCountResponseEntity":{"type":"object","properties":{"totalSharedMessages":{"type":"number","example":1250000},"totalMessages":{"type":"number","example":4800000},"totalWhatsappMessages":{"type":"number","example":900000},"totalVoicemailMessages":{"type":"number","example":1200},"totalCallLogs":{"type":"number","example":86000},"totalContacts":{"type":"number","example":410000},"totalDevices":{"type":"number","example":3200},"totalOrganizations":{"type":"number","example":640}},"required":["totalSharedMessages","totalMessages","totalWhatsappMessages","totalVoicemailMessages","totalCallLogs","totalContacts","totalDevices","totalOrganizations"]},"NewExtractionsReportResponseEntity":{"type":"object","properties":{"data":{"type":"array","items":{"type":"string"}},"total":{"type":"number","example":18}},"required":["data","total"]},"FetchNewDevicesReportDto":{"type":"object","properties":{}},"OrganizationReportEntity":{"type":"object","properties":{"organization":{"$ref":"#/components/schemas/OrganizationEntity"},"otherData":{"type":"object","example":{"accountAgeInDays":420,"totalCases":12,"totalExtractionCodes":30,"unusedExtractionCodes":4,"totalUniqueDevices":22}}},"required":["organization","otherData"]},"ExtractionStatusItemEntity":{"type":"object","properties":{"extractionCode":{"type":"string","example":"AB12CD"},"extractionCodeId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e8"},"sourceId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e9"},"status":{"type":"string","enum":["pending","in_progress","staged","completed","failed"],"example":"in_progress"},"sourceType":{"type":"string","example":"device"},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"},"lastStatusChangedAt":{"format":"date-time","type":"string","example":"2026-01-15T11:00:00.000Z"},"organization":{"type":"string","example":"Example Firm"},"organizationEmail":{"type":"string","example":"admin@example.com"},"watchlist":{"type":"boolean","example":false}},"required":["extractionCode","extractionCodeId","sourceId","status","sourceType","createdAt","lastStatusChangedAt","organization","organizationEmail","watchlist"]},"FetchUserEmailDataProviderReportDto":{"type":"object","properties":{"startDate":{"type":"string"},"endDate":{"type":"string"},"extractionCode":{"type":"string"},"caseId":{"type":"string"}}},"PhoneDeviceStorageLogItemEntity":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f3"},"deviceId":{"type":"string","example":"00008110-001A2B3C4D5E6F70"},"osVersion":{"type":"string","nullable":true,"example":"17.4"},"phoneHardwareVersion":{"type":"string","nullable":true,"example":"iPhone15,2"},"phoneOtherInfo":{"type":"object","nullable":true},"calculatedTotalSpaceUsed":{"type":"number","nullable":true,"example":96000000000},"userReportedTotalSpaceUsed":{"type":"number","nullable":true,"example":98000000000},"calculatedTotalDiskSpace":{"type":"number","nullable":true,"example":128000000000},"userReportedTotalDiskSpace":{"type":"number","nullable":true,"example":128000000000},"otherStorageMetadata":{"type":"object","nullable":true},"oldData":{"type":"array","items":{"type":"object"}},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"}},"required":["_id","deviceId","osVersion","phoneHardwareVersion","phoneOtherInfo","calculatedTotalSpaceUsed","userReportedTotalSpaceUsed","calculatedTotalDiskSpace","userReportedTotalDiskSpace","otherStorageMetadata","oldData","createdAt","updatedAt"]},"NotificationThreadEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"notificationTs":{"type":"string"},"channelName":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"type":"string"},"email":{"type":"string"},"case":{"$ref":"#/components/schemas/CaseEntity"},"organization":{"$ref":"#/components/schemas/OrganizationEntity"},"extractionCode":{"$ref":"#/components/schemas/ExtractionCodeEntity"},"device":{"$ref":"#/components/schemas/DeviceEntity"},"channelType":{"type":"string","enum":["account_management","backup","import","backup_logs","export","desktop_client","email_logs","other","case"]},"ecCode":{"type":"string"},"systemId":{"type":"string"},"connectedChannels":{"type":"object","default":null}},"required":["createdAt","updatedAt","id","notificationTs","channelName","deviceId","deviceType","email","case","organization","extractionCode","device","channelType","ecCode","systemId","connectedChannels"]},"DesktopClientEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"systemId":{"type":"string"},"ecCodes":{"type":"array","items":{"type":"string"}},"extractionCodes":{"description":"Ids of the extraction codes used on this computer","example":["65a1b2c3d4e5f6a7b8c9d0e1"],"type":"array","items":{"type":"string"}},"isOnline":{"type":"boolean"},"lastPingAt":{"format":"date-time","type":"string"},"lastSeenIp":{"type":"string"},"lastSeenLocation":{"type":"string"},"lastUsedAppVersion":{"type":"string"},"osDetails":{"$ref":"#/components/schemas/OSDetailsEntity"},"roomName":{"type":"string"},"connectedDevices":{"description":"Ids of the devices connected to this computer","example":["65a1b2c3d4e5f6a7b8c9d0e2"],"type":"array","items":{"type":"string"}},"latestState":{"type":"string"}},"required":["createdAt","updatedAt","id","systemId","ecCodes","extractionCodes","isOnline","lastPingAt","lastSeenIp","lastSeenLocation","lastUsedAppVersion","osDetails","roomName","connectedDevices","latestState"]},"NotificationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"notificationThreadTs":{"type":"string"},"blocks":{"type":"object"},"mainNotificationThread":{"$ref":"#/components/schemas/NotificationThreadEntity"},"case":{"$ref":"#/components/schemas/CaseEntity"},"organization":{"$ref":"#/components/schemas/OrganizationEntity"},"device":{"$ref":"#/components/schemas/DeviceEntity"},"client":{"$ref":"#/components/schemas/DesktopClientEntity"},"deviceId":{"type":"string"},"deviceType":{"type":"string"},"texts":{"default":[],"type":"array","items":{"type":"string"}},"links":{"default":[],"type":"array","items":{"type":"string"}},"channelName":{"type":"string"},"otherData":{"type":"object"},"systemId":{"type":"string"},"source":{"$ref":"#/components/schemas/MasterDataSourceEntity"}},"required":["createdAt","updatedAt","id","notificationThreadTs","blocks","mainNotificationThread","case","organization","device","client","deviceId","deviceType","texts","links","channelName","otherData","systemId","source"]},"UpdateLatestSubscriptionForOrgDto":{"type":"object","properties":{"orgId":{"type":"string","description":"required if orgOwnerEmail not sent"},"orgOwnerEmail":{"type":"string","description":"required if orgId not sent"}}},"UpdateOrgPlanDto":{"type":"object","properties":{"orgId":{"type":"string","description":"required if orgOwnerEmail not sent"},"orgOwnerEmail":{"type":"string","description":"required if orgId not sent"},"productId":{"type":"string"}},"required":["productId"]},"TagMigrationCollectionStatusEntity":{"type":"object","properties":{"name":{"type":"string","example":"devicemessages"},"total":{"type":"number","example":50000},"processed":{"type":"number","example":12000},"skipped":{"type":"number","example":40},"errored":{"type":"number","example":0},"startedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:15:00.000Z"},"completedAt":{"format":"date-time","type":"string","nullable":true,"example":null}},"required":["name","total","processed","skipped","errored"]},"TagMigrationStatusEntity":{"type":"object","properties":{"jobId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f2"},"status":{"type":"string","example":"running"},"collections":{"type":"array","items":{"$ref":"#/components/schemas/TagMigrationCollectionStatusEntity"}},"lastError":{"type":"string","nullable":true,"example":null}},"required":["jobId","status","collections"]},"TriggerTagMigrationDto":{"type":"object","properties":{"collections":{"description":"Specific collections to migrate. Empty/omitted = all 20.","type":"array","items":{"type":"string"}},"batchSize":{"type":"number","default":1000},"dryRun":{"type":"boolean","default":false}}},"BackfillTagSourcesDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Restrict the backfill to a single case. Omit to backfill all cases."}}},"FixSearchTagAttributionDto":{"type":"object","properties":{"providerId":{"type":"string","description":"Restrict the fix to a single email provider (mailbox) on the case."}}},"DeleteCaseFilesResponseEntity":{"type":"object","properties":{"matched":{"type":"number","description":"Case files in scope, including ones already deleted","example":4},"deleted":{"type":"number","description":"Case files soft-deleted by this call (0 on a retry)","example":4}},"required":["matched","deleted"]},"DeleteCaseFilesDto":{"type":"object","properties":{"caseFileIds":{"description":"CaseFile ids to delete (max 1000). Every id must be an uploaded file on the case (not a CMP-synced legal document). Omit when sending `all`.","type":"array","items":{"type":"string"}},"all":{"type":"boolean","enum":[true],"description":"Delete every uploaded case file on the case. Omit when sending `caseFileIds`."}}},"AdminBillingProfileResponseEntity":{"type":"object","properties":{"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"tier":{"type":"string","enum":["public","solo","firm","firm-plus"],"example":"firm"},"billingMode":{"type":"string","enum":["payg","per-source","flat-case"],"example":"payg"},"cadence":{"type":"string","enum":["monthly","annual"],"example":"monthly"},"billingEmail":{"type":"string","nullable":true,"example":"billing@example.com"},"paymentMethodOnFile":{"type":"boolean","example":true},"chargeTrigger":{"type":"string","enum":["backup","import"],"nullable":true,"description":"When this org's phone collections are charged. `null` means it follows the platform-wide flag rather than pinning a value.","example":null},"overrides":{"type":"object","nullable":true,"example":{"storageIncludedGB":100,"skuUnitAmountCents":{"collection.phone":10000}}},"warnings":{"description":"Non-blocking notices, e.g. a per-SKU override the tier/mode already includes","example":[],"type":"array","items":{"type":"string"}}},"required":["organization","tier","billingMode","cadence","billingEmail","paymentMethodOnFile","chargeTrigger","overrides","warnings"]},"AdminBillingProfileOverridesDto":{"type":"object","properties":{"storageIncludedGB":{"type":"number"},"skuUnitAmountCents":{"type":"object","description":"SKU key -> cents. 0 = free for this org; null = remove the override and fall back to the catalog/tier price."},"perCaseSourceCap":{"type":"number","description":"Provisioned only — NOT enforced in v1 (spec locked decision); enforcement is a later phase"}}},"UpdateAdminBillingProfileDto":{"type":"object","properties":{"tier":{"type":"string","enum":["public","solo","firm","firm-plus"]},"billingMode":{"type":"string","enum":["payg","per-source","flat-case"]},"cadence":{"type":"string","enum":["monthly","annual"]},"billingEmail":{"type":"string"},"paymentMethodOnFile":{"type":"boolean"},"chargeTrigger":{"type":"string","enum":["backup","import"],"nullable":true,"description":"When this org's phone collections are charged. `null` clears the override and returns the org to the platform-wide flag; `import` PINS it to import-time charging even after that flag flips."},"overrides":{"$ref":"#/components/schemas/AdminBillingProfileOverridesDto"},"reason":{"type":"string","description":"Required audit reason — persisted on the BillingEvent"}},"required":["reason"]},"AdminOrgBillingMigrationResponseEntity":{"type":"object","properties":{"profileCreated":{"type":"boolean","description":"Always true — the org is now on case billing","example":true},"paymentMethodOnFile":{"type":"boolean","description":"Whether the org had a Stripe default payment method","example":true},"legacySubscriptionCanceled":{"type":"boolean","description":"Whether a live legacy Stripe subscription was canceled by this migration","example":true}},"required":["profileCreated","paymentMethodOnFile","legacySubscriptionCanceled"]},"MigrateOrgBillingDto":{"type":"object","properties":{"tier":{"type":"string","enum":["public","solo","firm","firm-plus"]},"billingMode":{"type":"string","enum":["payg","per-source","flat-case"]},"cadence":{"type":"string","enum":["monthly","annual"]},"overrides":{"$ref":"#/components/schemas/AdminBillingProfileOverridesDto"},"billingEmail":{"type":"string"}},"required":["tier","billingMode","cadence"]},"AdminBillingItemResponseEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e9"},"caseId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"skuKey":{"type":"string","example":"addon.shipping"},"status":{"type":"string","example":"PENDING"},"description":{"type":"string","example":"Admin add-on: addon.shipping"},"quantity":{"type":"number","example":1},"unitAmountCents":{"type":"number","example":2500},"amountCents":{"type":"number","example":2500},"informational":{"type":"boolean","example":false},"listAmountCents":{"type":"number","nullable":true,"example":null},"adminNote":{"type":"string","nullable":true,"example":null}},"required":["id","caseId","skuKey","status","description","quantity","unitAmountCents","amountCents","informational","listAmountCents","adminNote"]},"CreateAdminBillingItemDto":{"type":"object","properties":{"skuKey":{"type":"string","enum":["collection.phone","collection.social","collection.email","collection.ai-chat","collection.financial","collection.discord","collection.reddit","collection.linkedin","collection.legal","collection.file","collection.other","case.flat","case.pro-upgrade","addon.kit-rental","addon.laptop-rental","addon.laptop-rental-setup","addon.managed-service","addon.shipping","addon.live-bank","plan.base","source.per","ai.tokens-overage","adjustment.manual","adjustment.discount"]},"quantity":{"type":"number","minimum":1},"requestId":{"type":"string","description":"Client-supplied dedup token — repeating the same requestId for the same case+SKU returns the existing row untouched"},"reason":{"type":"string","description":"Required audit reason — persisted on the BillingEvent"},"comped":{"type":"boolean","description":"Create the row already COMPED (never swept/invoiced) instead of billable"},"unitAmountCentsOverride":{"type":"number","description":"Per-item price override — only honored for addon.shipping/adjustment.manual (negative allowed on adjustment.manual only)"}},"required":["skuKey","quantity","requestId","reason"]},"AdminProUpgradeDto":{"type":"object","properties":{"comped":{"type":"boolean","enum":[true]},"reason":{"type":"string","description":"Required audit reason — persisted on the BillingEvent"}},"required":["comped","reason"]},"AdminBillingItemActionDto":{"type":"object","properties":{"reason":{"type":"string","description":"Required audit reason — persisted as adminNote + on the BillingEvent"}},"required":["reason"]},"AdminTokenGrantResponseEntity":{"type":"object","properties":{"caseId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"granted":{"type":"number","example":1000000},"allowance":{"type":"number","example":3000000},"used":{"type":"number","example":250000}},"required":["caseId","granted","allowance","used"]},"GrantAiTokensDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Id of the case to grant tokens on"},"tokens":{"type":"number","minimum":1},"reason":{"type":"string","description":"Required audit reason — persisted on the BillingEvent"},"requestId":{"type":"string","description":"Client-supplied dedup token — repeating the same requestId for the same case returns the prior grant untouched instead of granting a second time. Requires the organization to already have a BillingProfile to be enforced."}},"required":["caseId","tokens","reason"]},"AdminPlanChangeResponseEntity":{"type":"object","properties":{"plan":{"$ref":"#/components/schemas/SelectedPlanResponseEntity"},"prorationCents":{"type":"number","description":"Net cents charged (positive) or credited (negative) now to reconcile the base fee on a plan change; 0 on a first-time plan assignment.","example":0},"paymentRequired":{"type":"boolean","description":"Informational only: true if the org's (new) tier is paid and it has no card on file yet. Staff cannot capture a card here — the org must add one via its own self-serve flow.","example":false}},"required":["plan","prorationCents","paymentRequired"]},"AdminChangePlanDto":{"type":"object","properties":{"tier":{"type":"string","enum":["public","solo","firm","firm-plus"]},"cadence":{"type":"string","enum":["monthly","annual"]}},"required":["tier"]},"GrantCreditDto":{"type":"object","properties":{"amountCents":{"type":"number","minimum":1,"description":"Credit to add, in integer cents"},"reason":{"type":"string","description":"Required audit reason — persisted on the ledger"},"requestId":{"type":"string","description":"Client-supplied dedup token — repeating the same requestId returns the prior grant instead of double-crediting"}},"required":["amountCents","reason"]},"PrepaidProfileStateResponseEntity":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether this org has prepaid settlement at all","example":true},"allowUnpaid":{"type":"boolean","description":"Support override: lets collections through unpaid","example":false},"allowUnpaidReason":{"type":"string","nullable":true,"description":"Required when allowUnpaid is true; null otherwise","example":null}},"required":["enabled","allowUnpaid","allowUnpaidReason"]},"PrepaidBalanceResponseEntity":{"type":"object","properties":{"skuKey":{"type":"string","enum":["collection.phone","collection.social","collection.email","collection.ai-chat","collection.financial","collection.discord","collection.reddit","collection.linkedin","collection.legal","collection.file","collection.other","case.flat","case.pro-upgrade","addon.kit-rental","addon.laptop-rental","addon.laptop-rental-setup","addon.managed-service","addon.shipping","addon.live-bank","plan.base","source.per","ai.tokens-overage","adjustment.manual","adjustment.discount"],"example":"collection.phone"},"unitsRemaining":{"type":"number","example":3}},"required":["skuKey","unitsRemaining"]},"PrepaidLedgerEntryResponseEntity":{"type":"object","properties":{"skuKey":{"type":"string","enum":["collection.phone","collection.social","collection.email","collection.ai-chat","collection.financial","collection.discord","collection.reddit","collection.linkedin","collection.legal","collection.file","collection.other","case.flat","case.pro-upgrade","addon.kit-rental","addon.laptop-rental","addon.laptop-rental-setup","addon.managed-service","addon.shipping","addon.live-bank","plan.base","source.per","ai.tokens-overage","adjustment.manual","adjustment.discount"],"example":"collection.phone"},"deltaUnits":{"type":"number","description":"Positive for a grant, negative for a consume/revoke","example":5},"balanceAfterUnits":{"type":"number","example":5},"balanceApplied":{"type":"boolean","description":"False means the row is RESERVED: its ledger append landed but its balance move has not. `balanceAfterUnits` is a placeholder 0 on such a row.","example":true},"reason":{"type":"string","enum":["grant","consume","revoke","restore","admin_grant"],"example":"grant"},"description":{"type":"string","example":"Purchased 5 phone collections"},"productKey":{"type":"string","nullable":true,"example":null},"quantity":{"type":"number","nullable":true,"example":5},"amountPaidCents":{"type":"number","nullable":true,"description":"Integer cents","example":50000},"currency":{"type":"string","example":"usd"},"sourceCase":{"type":"string","nullable":true,"example":null},"sourceExtractionCode":{"type":"string","nullable":true,"example":null},"stripeChargeId":{"type":"string","nullable":true,"example":"ch_3Q2w3E4r5T6y7U8i"},"createdBy":{"type":"string","nullable":true,"example":null},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"}},"required":["skuKey","deltaUnits","balanceAfterUnits","balanceApplied","reason","description","productKey","quantity","amountPaidCents","currency","sourceCase","sourceExtractionCode","stripeChargeId","createdBy","createdAt"]},"PrepaidStateResponseEntity":{"type":"object","properties":{"prepaid":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/PrepaidProfileStateResponseEntity"}]},"balances":{"type":"array","items":{"$ref":"#/components/schemas/PrepaidBalanceResponseEntity"}},"ledger":{"type":"array","items":{"$ref":"#/components/schemas/PrepaidLedgerEntryResponseEntity"}}},"required":["prepaid","balances","ledger"]},"UpdatePrepaidOverrideDto":{"type":"object","properties":{"allowUnpaid":{"type":"boolean","description":"Waive the prepaid unit requirement for this org when true; setting it back to false also clears the stored reason"},"reason":{"type":"string","description":"Required audit reason — persisted on the profile while the override is set and shown on the statement. The override lets collections through unpaid, so an unexplained one is indistinguishable from a bug six months later."}},"required":["allowUnpaid","reason"]},"AdminBillingSweepRunResponseEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f0"},"startedAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"},"finishedAt":{"format":"date-time","type":"string","example":"2026-01-15T10:20:00.000Z"},"durationMs":{"type":"number","example":300000},"status":{"type":"string","enum":["succeeded","failed"],"example":"succeeded"},"manual":{"type":"boolean","description":"true for an admin-triggered run, false for the nightly cron","example":false},"organizationId":{"type":"string","nullable":true,"description":"Set when the run was scoped to one org","example":null},"invoicesCreated":{"type":"number","example":3},"totalCents":{"type":"number","example":45000},"groupsFailed":{"type":"number","example":0},"stuckReset":{"type":"number","example":0},"baseFeeRowsCreated":{"type":"number","example":2},"recurringAddonRowsCreated":{"type":"number","example":1},"failedGroups":{"description":"Which group failed and why — the detail behind groupsFailed","example":[],"type":"array","items":{"type":"string"}},"error":{"type":"string","nullable":true,"description":"Set only when the sweep itself threw","example":null}},"required":["id","startedAt","finishedAt","durationMs","status","manual","organizationId","invoicesCreated","totalCents","groupsFailed","stuckReset","baseFeeRowsCreated","recurringAddonRowsCreated","failedGroups","error"]},"TriggerBillingSweepDto":{"type":"object","properties":{"asOf":{"type":"string","description":"ISO date the sweep bills for (default: now)"},"organizationId":{"type":"string","description":"Scope the sweep to one organization (default: all)"}}},"CloseBillingMonthDto":{"type":"object","properties":{"yearMonth":{"type":"string","description":"UTC month YYYY-MM (default: current month)"},"organizationId":{"type":"string","description":"Scope the close to one organization (default: all)"}}},"TriggerReconciliationDto":{"type":"object","properties":{"asOf":{"type":"string","description":"ISO date to reconcile as of (default: now)"}}},"AdminCouponResponseEntity":{"type":"object","properties":{"couponId":{"type":"string","description":"Stripe coupon id — the redeemable code","example":"SPRING20"},"name":{"type":"string","nullable":true,"example":"Spring 20% off"},"percentOff":{"type":"number","nullable":true,"example":20},"amountOffCents":{"type":"number","nullable":true,"example":null},"currency":{"type":"string","nullable":true,"example":null},"duration":{"type":"string","enum":["once","repeating","forever"],"example":"repeating"},"durationInMonths":{"type":"number","nullable":true,"example":3},"maxRedemptions":{"type":"number","nullable":true,"example":50},"redeemBy":{"format":"date-time","type":"string","nullable":true,"example":"2026-06-30T23:59:59.000Z"},"timesRedeemed":{"type":"number","example":4},"status":{"type":"string","enum":["active","expired","deleted"],"description":"Derived from deletedAt/redeemBy at read time, never stored","example":"active"},"reason":{"type":"string","nullable":true,"example":"Spring promotion approved by sales"},"deletedAt":{"format":"date-time","type":"string","nullable":true,"example":null}},"required":["couponId","name","percentOff","amountOffCents","currency","duration","durationInMonths","maxRedemptions","redeemBy","timesRedeemed","status","reason","deletedAt"]},"CreateCouponDto":{"type":"object","properties":{"couponId":{"type":"string","description":"Stripe coupon id — THIS IS THE REDEEMABLE CODE customers type. Stripe generates a random one when omitted."},"name":{"type":"string","description":"Human-readable name shown on the invoice"},"percentOff":{"type":"number","description":"Percent discount, 1-100. Mutually exclusive with amountOffCents."},"amountOffCents":{"type":"number","description":"Fixed discount in cents. Requires currency."},"currency":{"type":"string","description":"ISO currency, required with amountOffCents","example":"usd"},"duration":{"type":"string","enum":["once","repeating","forever"]},"durationInMonths":{"type":"number","description":"Months to repeat for — required when duration is \"repeating\""},"maxRedemptions":{"type":"number","description":"Total redemptions allowed across all customers"},"redeemBy":{"format":"date-time","type":"string","description":"Cannot be applied after this instant (ISO 8601)"},"appliesToProducts":{"description":"Restrict the discount to these Stripe product ids","type":"array","items":{"type":"string"}},"metadata":{"type":"object"},"reason":{"type":"string","description":"Audit note — why this coupon was issued"}},"required":["duration"]},"AdminCouponListResponseEntity":{"type":"object","properties":{"coupons":{"type":"array","items":{"$ref":"#/components/schemas/AdminCouponResponseEntity"}},"total":{"type":"number","description":"Total matching the filter, ignoring limit/skip","example":1}},"required":["coupons","total"]},"AdminCouponStackEntryResponseEntity":{"type":"object","properties":{"couponId":{"type":"string","example":"SPRING20"},"pushedAt":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"},"name":{"type":"string","nullable":true,"example":"Spring 20% off"},"percentOff":{"type":"number","nullable":true,"example":20},"amountOffCents":{"type":"number","nullable":true,"example":null},"duration":{"type":"string","nullable":true,"example":"repeating"},"status":{"type":"string","description":"active/expired/deleted, or \"unknown\" when the coupon is missing from the local mirror","example":"active"},"eligible":{"type":"boolean","description":"Whether this entry could be the active discount","example":true},"active":{"type":"boolean","description":"The ONE entry currently mirrored onto the Stripe customer — the top eligible coupon","example":true}},"required":["couponId","pushedAt","name","percentOff","amountOffCents","duration","status","eligible","active"]},"AdminCouponStackResponseEntity":{"type":"object","properties":{"stack":{"description":"Top of the stack first","type":"array","items":{"$ref":"#/components/schemas/AdminCouponStackEntryResponseEntity"}},"activeCouponId":{"type":"string","nullable":true,"description":"The coupon currently discounting this org, if any","example":"SPRING20"}},"required":["stack","activeCouponId"]},"ApplyCouponDto":{"type":"object","properties":{"couponId":{"type":"string","description":"Stripe coupon id (the redeemable code)"}},"required":["couponId"]},"ReleaseDraftBillingItemDto":{"type":"object","properties":{"reason":{"type":"string"}}},"BillingReportStatusResponseEntity":{"type":"object","properties":{"reportId":{"type":"string"},"status":{"type":"string","enum":["QUEUED","PROCESSING","COMPLETED","FAILED"]},"downloadUrl":{"type":"string","nullable":true},"error":{"type":"string","nullable":true},"skippedOrganizations":{"type":"array","items":{"type":"string"}}},"required":["reportId","status","skippedOrganizations"]},"AdminBillingReportQueryDto":{"type":"object","properties":{"caseIds":{"description":"Defaults to all cases","type":"array","items":{"type":"string"}},"startDate":{"format":"date-time","type":"string","description":"Defaults to the start of the organization's current billing cadence period"},"endDate":{"format":"date-time","type":"string","description":"Defaults to now"},"format":{"type":"string","enum":["json","pdf"],"default":"json"},"orgIds":{"description":"Defaults to every organization that has a BillingProfile (i.e. is on case billing)","type":"array","items":{"type":"string"}}}},"AdminRecurringAddonResponseEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"organization":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"},"case":{"type":"string","nullable":true,"example":null},"skuKey":{"type":"string","enum":["collection.phone","collection.social","collection.email","collection.ai-chat","collection.financial","collection.discord","collection.reddit","collection.linkedin","collection.legal","collection.file","collection.other","case.flat","case.pro-upgrade","addon.kit-rental","addon.laptop-rental","addon.laptop-rental-setup","addon.managed-service","addon.shipping","addon.live-bank","plan.base","source.per","ai.tokens-overage","adjustment.manual","adjustment.discount"],"example":"addon.laptop-rental"},"recurrence":{"type":"string","enum":["monthly","semiannual"],"example":"monthly"},"startedAt":{"format":"date-time","type":"string","description":"Period anchor as well as the start date — every period falls on this day-of-month, not the 1st","example":"2026-01-15T10:15:00.000Z"},"canceledAt":{"format":"date-time","type":"string","nullable":true,"example":null},"status":{"type":"string","enum":["active","canceled"],"example":"active"},"externalRef":{"type":"string","description":"Caller-supplied dedup token","example":"rental-2026-001"}},"required":["id","organization","case","skuKey","recurrence","startedAt","canceledAt","status","externalRef"]},"ActivateRecurringAddonDto":{"type":"object","properties":{"skuKey":{"type":"string","enum":["collection.phone","collection.social","collection.email","collection.ai-chat","collection.financial","collection.discord","collection.reddit","collection.linkedin","collection.legal","collection.file","collection.other","case.flat","case.pro-upgrade","addon.kit-rental","addon.laptop-rental","addon.laptop-rental-setup","addon.managed-service","addon.shipping","addon.live-bank","plan.base","source.per","ai.tokens-overage","adjustment.manual","adjustment.discount"],"description":"Must be a recurring add-on (kit rental, laptop rental, live bank). One-time charges go through the billing-item endpoint."},"externalRef":{"type":"string","description":"Caller-supplied dedup token. Repeating it for the same org+SKU returns the existing subscription instead of starting a second one. Reactivating after a cancel needs a NEW value."},"reason":{"type":"string","description":"Required audit reason — persisted on the BillingEvent"},"case":{"type":"string","description":"Case this add-on belongs to. Rentals are case-level; live banks may be org-level (omit)."},"startedAt":{"format":"date-time","type":"string","description":"When the subscription starts, ISO-8601. Defaults to now. Also the period anchor — every later period falls on this day-of-month, not the 1st."}},"required":["skuKey","externalRef","reason"]},"AdminRecurringAddonListResponseEntity":{"type":"object","properties":{"addons":{"type":"array","items":{"$ref":"#/components/schemas/AdminRecurringAddonResponseEntity"}},"total":{"type":"number","example":1}},"required":["addons","total"]},"CancelRecurringAddonDto":{"type":"object","properties":{"reason":{"type":"string","description":"Required audit reason — persisted on the BillingEvent"}},"required":["reason"]},"AdminFeatureFlagResponseEntity":{"type":"object","properties":{"key":{"type":"string","description":"The flag key, e.g. \"clio-integration\"","example":"clio-integration"},"enabled":{"type":"boolean","description":"Effective value. An UNSEEDED flag reads false because no row exists.","example":true},"description":{"type":"string","nullable":true,"example":"Turns on the Clio integration"},"status":{"type":"string","enum":["SEEDED","UNSEEDED","ORPHANED"],"description":"SEEDED = defined in code and backed by a row; UNSEEDED = defined in code, no row yet (toggling creates it); ORPHANED = a leftover row this build no longer defines, so it cannot be toggled. Derived at read time, never stored.","example":"SEEDED"},"updatedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:15:00.000Z"},"updatedByEmail":{"type":"string","nullable":true,"description":"Admin who last toggled the flag. Null on rows a seed migration created and no one has changed since.","example":"admin@example.com"}},"required":["key","enabled","description","status","updatedAt","updatedByEmail"]},"AdminFeatureFlagListResponseEntity":{"type":"object","properties":{"flags":{"type":"array","items":{"$ref":"#/components/schemas/AdminFeatureFlagResponseEntity"}},"total":{"type":"number","description":"Number of flags returned","example":24}},"required":["flags","total"]},"UpdateFeatureFlagDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Desired state of the flag. Sending the value it already has is a no-op and leaves the audit fields untouched."}},"required":["enabled"]},"UploadLogOpenDto":{"type":"object","properties":{"systemId":{"type":"string"},"date":{"type":"string","description":"ISO date string"},"appVersion":{"type":"string","description":"App version"},"message":{"type":"string","description":"Message"},"osDetails":{"type":"string"},"extractionCode":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"type":"string"}},"required":["systemId","date"]},"UploadDeviceLogResponseEntity":{"type":"object","properties":{"message":{"type":"string"},"isSuccess":{"type":"boolean"},"threadTs":{"type":"string"}},"required":["message","isSuccess","threadTs"]},"UploadAppLogDto":{"type":"object","properties":{"systemId":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"date":{"type":"string","description":"ISO date string"},"appVersion":{"type":"string","description":"App version"},"message":{"type":"string","description":"Message"},"osDetails":{"type":"string"},"deviceMetadata":{"type":"string"},"threadTs":{"type":"string"},"replyBroadcast":{"type":"boolean"},"backupMetadata":{"type":"object"},"errorMetadata":{"type":"string"},"backupPath":{"type":"string"}},"required":["systemId","deviceId","deviceType","date"]},"UploadLogOpenDtoV2":{"type":"object","properties":{"systemId":{"type":"string"},"date":{"type":"string","description":"ISO date string"},"appVersion":{"type":"string","description":"App version"},"message":{"type":"string","description":"Message"},"osDetails":{"type":"object"},"extractionCode":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"type":"string"},"fileProviderId":{"type":"string"}},"required":["systemId","date","fileProviderId"]},"UploadAppLogDtoV2":{"type":"object","properties":{"systemId":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"date":{"type":"string","description":"ISO date string"},"appVersion":{"type":"string","description":"App version"},"message":{"type":"string","description":"Message"},"osDetails":{"type":"object"},"deviceMetadata":{"type":"object"},"threadTs":{"type":"string"},"replyBroadcast":{"type":"boolean"},"backupMetadata":{"type":"object"},"errorMetadata":{"type":"object"},"backupPath":{"type":"string"},"fileProviderId":{"type":"string"}},"required":["systemId","deviceId","deviceType","date","fileProviderId"]},"SingleDeviceExtractionStatusResponseEntity":{"type":"object","properties":{"lastBackupAt":{"format":"date-time","type":"string"},"hasNewEcCodeConfig":{"type":"boolean"},"deviceId":{"type":"string"},"deviceType":{"type":"string"}},"required":["lastBackupAt","hasNewEcCodeConfig","deviceId","deviceType"]},"DevicesExtractionStatusResponseEntity":{"type":"object","properties":{"devices":{"type":"array","items":{"$ref":"#/components/schemas/SingleDeviceExtractionStatusResponseEntity"}}},"required":["devices"]},"FetchSingleDeviceExtractionStatusDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string"},"systemId":{"type":"string"}},"required":["deviceId","deviceType","systemId"]},"FetchDevicesExtractionStatusDto":{"type":"object","properties":{"devices":{"type":"array","items":{"$ref":"#/components/schemas/FetchSingleDeviceExtractionStatusDto"}}},"required":["devices"]},"LoginExtractionCodeDto":{"type":"object","properties":{"extractionCode":{"type":"string"},"appConfig":{"$ref":"#/components/schemas/CreateUpdateAppConfigDto"},"app":{"type":"string"},"os":{"type":"string"},"arch":{"type":"string"},"isEmailLogin":{"type":"boolean"},"client":{"type":"string","enum":["desktop","portal"],"description":"Caller type; the web portal sends \"portal\"."}},"required":["extractionCode","appConfig","app","os","arch","isEmailLogin"]},"DeviceBackupUpdateResponseEntity":{"type":"object","properties":{"recorded":{"type":"boolean","description":"Whether the lifecycle report was persisted."},"status":{"type":"string","enum":["started","completed","failed","cancelled"]},"billingOutcome":{"type":"string","enum":["not-applicable","trigger-is-import","no-organization","not-a-phone","device-not-registered","no-data-source-status","dispatching","dispatched","failed","report-failed"],"description":"What the backup-time charge dispatch did. Anything other than `dispatched` leaves the import-time charge as the backstop."}},"required":["recorded","status","billingOutcome"]},"DeviceBackupUpdateDto":{"type":"object","properties":{"systemId":{"type":"string"},"deviceId":{"type":"string","description":"Stable hardware id, not the Mongo _id"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"status":{"type":"string","enum":["started","completed","failed","cancelled"],"description":"Lifecycle state being reported. Only `completed` can result in a charge."},"reason":{"type":"string","description":"Cause for failed/cancelled"},"metadata":{"type":"object"},"appVersion":{"type":"string"}},"required":["deviceId","deviceType","status"]},"UpdateDeviceStorageDto":{"type":"object","properties":{"deviceId":{"type":"string"},"osVersion":{"type":"string"},"phoneHardwareVersion":{"type":"string"},"phoneOtherInfo":{"type":"object"},"calculatedTotalSpaceUsed":{"type":"number"},"calculatedTotalDiskSpace":{"type":"number"},"otherStorageMetadata":{"type":"object"}},"required":["deviceId"]},"DeviceStorageFeedbackDto":{"type":"object","properties":{"deviceId":{"type":"string"},"userReportedTotalSpaceUsed":{"type":"number"},"userReportedTotalDiskSpace":{"type":"number"}},"required":["deviceId","userReportedTotalSpaceUsed"]},"AddUpdateDeviceResponseEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"deviceId":{"type":"string","example":"3F2504E0-4F89-11D3-9A0C-0305E82C3301"},"serialNumber":{"type":"string","example":"F2LXK0QJHG7F"},"name":{"type":"string","example":"Jane's iPhone"},"type":{"type":"string","example":"ios"},"nonDeviceType":{"type":"string","nullable":true,"example":null},"model":{"type":"string","example":"iPhone 15"},"timeZone":{"type":"string","example":"America/New_York"},"osVersion":{"type":"string","example":"18.0"},"owner":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"devicePhoneNumber":{"type":"string","example":"+15555550100"},"availableData":{"type":"object","description":"Which data types exist for this device (optionally scoped by query filters).","example":{"messages":true,"calls":false}}},"required":["createdAt","updatedAt","id","deviceId","serialNumber","name","type","nonDeviceType","model","timeZone","osVersion","owner","devicePhoneNumber"]},"AddUpdateDeviceDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"deviceMetadata":{"type":"object"}},"required":["deviceId","deviceType"]},"UpdateExtractionCodeMetadataDto":{"type":"object","properties":{"metadata":{"type":"object"}},"required":["metadata"]},"SlackNotificationResponseEntity":{"type":"object","properties":{"ts":{"type":"string","example":"1700000000.000100"}},"required":["ts"]},"PostUpdateNotificationDto":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"string","minLength":1}},"deviceId":{"type":"string"},"deviceType":{"type":"string"},"shouldSendNotification":{"type":"boolean"}},"required":["messages"]},"PostTrackingNotificationDto":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"string","minLength":1}},"threadTs":{"type":"string"},"systemId":{"type":"string"},"date":{"type":"string","description":"ISO date string"},"appVersion":{"type":"string","description":"App version"},"osDetails":{"type":"object"}},"required":["messages","systemId","date"]},"UploadConversationsNotificationsDto":{"type":"object","properties":{"messageSelectionReport":{"$ref":"#/components/schemas/MessageSelectionReportDto"},"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"extractionCode":{"type":"string"},"appVersion":{"type":"string"},"noOfConversations":{"type":"number"},"message":{"type":"string"},"threadTs":{"type":"string"}},"required":["deviceId","deviceType","extractionCode","appVersion","noOfConversations","message"]},"PostDebugLogNotificationDto":{"type":"object","properties":{"systemId":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"appVersion":{"type":"string","description":"App version"},"logPath":{"type":"string"},"message":{"type":"string","description":"Message"}},"required":["systemId","deviceId","deviceType","logPath"]},"AppSettingsResponseEntity":{"type":"object","properties":{"localConversionEnabled":{"type":"boolean","description":"Local conversion status","example":true},"localCompressionEnabled":{"type":"boolean","description":"Local compression status","example":true},"collectionEventsEnabled":{"type":"boolean","description":"Send collection events + heartbeats","example":false}},"required":["localConversionEnabled","localCompressionEnabled","collectionEventsEnabled"]},"AddCaseFilesDto":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string"}},"category":{"type":"string"}},"required":["files"]},"ImportCaseMediaAlbumDto":{"type":"object","properties":{"id":{"type":"number"},"name":{"type":"string"}},"required":["id","name"]},"ImportCaseMediaFileDto":{"type":"object","properties":{"id":{"type":"number"},"fileName":{"type":"string"},"createdAt":{"type":"string"},"type":{"type":"number"},"duration":{"type":"number"},"uniformType":{"type":"string"},"trashedState":{"type":"number"},"directory":{"type":"string"},"album":{"$ref":"#/components/schemas/ImportCaseMediaAlbumDto"},"originalFileProviderName":{"type":"string"},"thumbnailFileProviderName":{"type":"string"},"metadata":{"type":"object"}},"required":["id","fileName"]},"ImportCaseMediaDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"deviceMetadata":{"type":"object"},"systemId":{"type":"string"},"appVersion":{"type":"string"},"osDetails":{"$ref":"#/components/schemas/OsDetailsDto"},"files":{"type":"array","items":{"$ref":"#/components/schemas/ImportCaseMediaFileDto"}}},"required":["deviceId","deviceType","systemId","appVersion","files"]},"ImportCaseNoteItemDto":{"type":"object","properties":{"id":{"type":"number"},"title":{"type":"string"},"summary":{"type":"string"},"createdAt":{"type":"string"},"modifiedAt":{"type":"string"},"htmlContent":{"type":"string"},"metadata":{"type":"object"},"attachments":{"type":"array","items":{"type":"string"}}},"required":["id","title"]},"ImportCaseNotesDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"deviceMetadata":{"type":"object"},"systemId":{"type":"string"},"appVersion":{"type":"string"},"osDetails":{"$ref":"#/components/schemas/OsDetailsDto"},"notes":{"type":"array","items":{"$ref":"#/components/schemas/ImportCaseNoteItemDto"}}},"required":["deviceId","deviceType","systemId","appVersion","notes"]},"ImportCaseBrowserHistoryDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"deviceMetadata":{"type":"object"},"systemId":{"type":"string"},"appVersion":{"type":"string"},"osDetails":{"$ref":"#/components/schemas/OsDetailsDto"},"safariHistoryFileKey":{"type":"string","description":"S3 key for the uploaded Safari History.db file"}},"required":["deviceId","deviceType","systemId","appVersion","safariHistoryFileKey"]},"ImportCallLogContactDto":{"type":"object","properties":{"id":{"type":"object"},"fullName":{"type":"string"},"name":{"type":"string"},"first":{"type":"string"},"middle":{"type":"string"},"last":{"type":"string"},"email":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"phoneNumbers":{"type":"string"},"iphone":{"type":"string"},"phoneWork":{"type":"string"},"phoneMobile":{"type":"string"},"phoneHome":{"type":"string"},"profileImagePath":{"type":"string"}},"required":["id"]},"ImportCallLogMessageDto":{"type":"object","properties":{"id":{"type":"object"},"duration":{"type":"object"},"date":{"type":"string"},"sender":{"type":"string"},"address":{"type":"string"},"callSource":{"type":"string"},"contact":{"type":"object"},"isFromMe":{"type":"boolean"},"isDelivered":{"type":"boolean"},"isDeleted":{"type":"boolean"},"isArchive":{"type":"boolean"},"isFinished":{"type":"boolean"},"isSpam":{"type":"boolean"},"isSticker":{"type":"boolean"},"isEmpty":{"type":"boolean"}},"required":["id","date","isFromMe"]},"ImportCallLogConversationDto":{"type":"object","properties":{"id":{"type":"object"},"guid":{"type":"string"},"chatName":{"type":"string"},"displayName":{"type":"string"},"name":{"type":"string"},"number":{"type":"string"},"lastMessageId":{"type":"object"},"date":{"type":"object"},"contacts":{"type":"array","items":{"$ref":"#/components/schemas/ImportCallLogContactDto"}},"sendersNotInContact":{"type":"array","items":{"type":"string"}},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ImportCallLogMessageDto"}}},"required":["id","messages"]},"ImportCaseCallLogsDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["unknown","android","ios","facebook_dump","instagram_dump","threads_dump","email"]},"deviceMetadata":{"type":"object"},"systemId":{"type":"string"},"appVersion":{"type":"string"},"osDetails":{"$ref":"#/components/schemas/OsDetailsDto"},"conversations":{"type":"array","items":{"$ref":"#/components/schemas/ImportCallLogConversationDto"}}},"required":["deviceId","deviceType","systemId","appVersion","conversations"]},"CaseFilesImportedCountEntity":{"type":"object","properties":{"totalFilesImported":{"type":"number","description":"Total number of files imported","example":100},"totalSizeImported":{"type":"number","description":"Total size of files imported","example":1000000}},"required":["totalFilesImported","totalSizeImported"]},"UserEmailDataEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"emailDataProvider":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"sourceEmail":{"type":"string","example":"jane.doe@example.com"},"body":{"type":"string","example":"<p>Please find the signed agreement attached.</p>","nullable":true},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"attachments":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/UserFileEntity"}},"bcc":{"example":[],"type":"array","items":{"type":"object"}},"cc":{"example":[{"name":"Sam Lee","email":"sam.lee@example.com"}],"type":"array","items":{"type":"object"}},"from":{"example":[{"name":"John Roe","email":"john.roe@example.org"}],"type":"array","items":{"type":"object"}},"replyTo":{"example":[],"type":"array","items":{"type":"object"}},"to":{"example":[{"name":"Jane Doe","email":"jane.doe@example.com"}],"type":"array","items":{"type":"object"}},"folders":{"example":["INBOX"],"type":"array","items":{"type":"string"}},"grantId":{"type":"string","example":"5f0c8b3e-2a41-4d7c-9a51-1c2b3d4e5f60"},"externalId":{"type":"string","example":"18d1f2a3b4c5d6e7"},"object":{"type":"string","example":"message","nullable":true},"snippet":{"type":"string","example":"Please find the signed agreement attached.","nullable":true},"starred":{"type":"boolean","example":false},"unread":{"type":"boolean","example":false},"subject":{"type":"string","example":"Signed agreement","nullable":true},"threadId":{"type":"string","example":"18d1f2a3b4c5d6e0"},"threadIndex":{"type":"number","example":0},"tags":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/TagEntity"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["createdAt","updatedAt","id","emailDataProvider","sourceEmail","body","date","attachments","bcc","cc","from","replyTo","to","folders","grantId","externalId","object","snippet","starred","unread","subject","threadId","threadIndex","tags"]},"UserEmailDataThreadEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"sourceEmail":{"type":"string","example":"jane.doe@example.com"},"grantId":{"type":"string","example":"5f0c8b3e-2a41-4d7c-9a51-1c2b3d4e5f60"},"externalId":{"type":"string","example":"18d1f2a3b4c5d6e0"},"lastMessageOrDraft":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"object","allOf":[{"$ref":"#/components/schemas/UserEmailDataEntity"}]},"emailCount":{"type":"number","description":"Number of emails in the thread","example":3},"hasAttachments":{"type":"boolean","example":true},"hasDrafts":{"type":"boolean","example":false},"earliestMessageDate":{"format":"date-time","type":"string","example":"2026-01-12T09:05:00.000Z","nullable":true},"latestMessageReceivedDate":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"latestMessageSentDate":{"format":"date-time","type":"string","example":"2026-01-14T16:45:00.000Z","nullable":true},"participants":{"example":[{"name":"Jane Doe","email":"jane.doe@example.com"},{"name":"John Roe","email":"john.roe@example.org"}],"type":"array","items":{"type":"object"}},"folders":{"example":["INBOX"],"type":"array","items":{"type":"string"}},"messageIds":{"example":["18d1f2a3b4c5d6e0","18d1f2a3b4c5d6e5","18d1f2a3b4c5d6e7"],"type":"array","items":{"type":"string"}},"draftIds":{"example":[],"type":"array","items":{"type":"string"}},"snippet":{"type":"string","example":"Please find the signed agreement attached.","nullable":true},"starred":{"type":"boolean","example":false},"unread":{"type":"boolean","example":true},"subject":{"type":"string","example":"Signed agreement","nullable":true},"tags":{"nullable":true,"description":"The full record on routes that load it; null (or an empty list) on routes that do not.","type":"array","items":{"$ref":"#/components/schemas/TagEntity"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}},"ocSharedStatus":{"type":"string","nullable":true,"enum":["shared","pending","not_gated"],"description":"Opposing-council only: whether this thread has been shared with the OL. `null` for OL/OG callers — same convention as cases/files/:caseId and conversation/search-entries."}},"required":["createdAt","updatedAt","id","sourceEmail","grantId","externalId","lastMessageOrDraft","emailCount","hasAttachments","hasDrafts","earliestMessageDate","latestMessageReceivedDate","latestMessageSentDate","participants","folders","messageIds","draftIds","snippet","starred","unread","subject","tags"]},"EmailDomainEntity":{"type":"object","properties":{"domain":{"type":"string","description":"The email domain (part after @), lowercased.","example":"example.org"},"count":{"type":"number","description":"Number of emails with this domain on a from/to/cc/bcc.","example":128}},"required":["domain","count"]},"EmailDomainsResponseEntity":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailDomainEntity"}},"totalDomains":{"type":"number","description":"Number of distinct domains.","example":12}},"required":["data","totalDomains"]},"ThreadByEmailResponseEntity":{"type":"object","properties":{"thread":{"$ref":"#/components/schemas/UserEmailDataThreadEntity"},"emailIds":{"example":["65a1b2c3d4e5f6a7b8c9d0c3","65a1b2c3d4e5f6a7b8c9d0c2"],"type":"array","items":{"type":"string"}}},"required":["thread","emailIds"]},"LinkTokenResponseEntity":{"type":"object","properties":{"linkToken":{"type":"string","description":"Temporary token to initialize Plaid Link","example":"link-sandbox-af1a0311-da53-4636-b754-dd15cc058176"},"expiration":{"type":"string","description":"Expiration date (ISO 8601)","example":"2026-01-15T18:30:00Z"},"requestId":{"type":"string","description":"ID for troubleshooting with Plaid support","example":"XQVgFigpGHXkb0b"},"hostedLinkUrl":{"type":"string","description":"Only present when using Hosted Link","example":"https://secure.plaid.com/hl/example-session"},"userId":{"type":"string","description":"Returned if Plaid created a new user","example":"usr_8c3ZbDBYjaqUXZ"}},"required":["linkToken","expiration","requestId"]},"PlaidLinkUserDto":{"type":"object","properties":{"legalName":{"type":"string"},"phoneNumber":{"type":"string"},"emailAddress":{"type":"string"},"dateOfBirth":{"type":"string","description":"Format: YYYY-MM-DD"}}},"PlaidStatementsConfigDto":{"type":"object","properties":{"startDate":{"type":"string","description":"Start date for statements extraction (YYYY-MM-DD). Maximum 2 years back. Defaults to 2 years ago."},"endDate":{"type":"string","description":"End date for statements extraction (YYYY-MM-DD). Maximum 2 years range. Defaults to today."}}},"PlaidAccountSubtypesDto":{"type":"object","properties":{"accountSubtypes":{"type":"array","items":{"type":"string"}}}},"PlaidAccountFiltersDto":{"type":"object","properties":{"depository":{"$ref":"#/components/schemas/PlaidAccountSubtypesDto"},"credit":{"$ref":"#/components/schemas/PlaidAccountSubtypesDto"},"loan":{"$ref":"#/components/schemas/PlaidAccountSubtypesDto"},"investment":{"$ref":"#/components/schemas/PlaidAccountSubtypesDto"}}},"CreateLinkTokenDto":{"type":"object","properties":{"products":{"type":"array","default":["transactions"],"description":"Plaid products to require. Defaults to [transactions, liabilities].","items":{"type":"string","enum":["assets","auth","beacon","employment","identity","income_verification","identity_verification","investments","liabilities","payment_initiation","standing_orders","signal","statements","transactions","transfer","cra_base_report","cra_income_insights","cra_cashflow_insights","cra_lend_score","cra_partner_insights","cra_network_insights","cra_monitoring","layer","protect_linked_bank"]}},"language":{"type":"string","default":"en","description":"Language for Plaid Link UI (en, es, fr, etc.)"},"countryCodes":{"default":["US"],"description":"ISO-3166-1 alpha-2 country codes.","type":"array","items":{"type":"string"}},"webhook":{"type":"string","description":"URL to receive Plaid webhooks"},"redirectUri":{"type":"string","description":"Redirect URI for OAuth flows"},"linkCustomizationName":{"type":"string"},"accessToken":{"type":"string","description":"Existing access token — use only for update mode"},"enableMultiItemLink":{"type":"boolean","default":false},"user":{"$ref":"#/components/schemas/PlaidLinkUserDto"},"requiredIfSupportedProducts":{"type":"array","default":["investments","liabilities"],"description":"Products to enable only if supported by the institution. Defaults to [investments, statements]. These will not block Link if the institution does not support them.","items":{"type":"string","enum":["assets","auth","beacon","employment","identity","income_verification","identity_verification","investments","liabilities","payment_initiation","standing_orders","signal","statements","transactions","transfer","cra_base_report","cra_income_insights","cra_cashflow_insights","cra_lend_score","cra_partner_insights","cra_network_insights","cra_monitoring","layer","protect_linked_bank"]}},"statementsConfig":{"description":"Date range for statements extraction. Plaid maximum is 2 years. Defaults to 2 years ago → today.","allOf":[{"$ref":"#/components/schemas/PlaidStatementsConfigDto"}]},"optionalProducts":{"type":"array","description":"Products that improve UX but do not block item creation","items":{"type":"string","enum":["assets","auth","beacon","employment","identity","income_verification","identity_verification","investments","liabilities","payment_initiation","standing_orders","signal","statements","transactions","transfer","cra_base_report","cra_income_insights","cra_cashflow_insights","cra_lend_score","cra_partner_insights","cra_network_insights","cra_monitoring","layer","protect_linked_bank"]}},"additionalConsentedProducts":{"type":"array","description":"Products to collect consent for without immediate billing","items":{"type":"string","enum":["assets","auth","beacon","employment","identity","income_verification","identity_verification","investments","liabilities","payment_initiation","standing_orders","signal","statements","transactions","transfer","cra_base_report","cra_income_insights","cra_cashflow_insights","cra_lend_score","cra_partner_insights","cra_network_insights","cra_monitoring","layer","protect_linked_bank"]}},"accountFilters":{"$ref":"#/components/schemas/PlaidAccountFiltersDto"}}},"ExchangePublicTokenResponseEntity":{"type":"object","properties":{"financialAccountProviderId":{"type":"string","description":"FinancialAccountProvider document ID","example":"507f1f77bcf86cd799439011"},"plaidItemId":{"type":"string","description":"PlaidItem document ID","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"plaidExternalItemId":{"type":"string","description":"Unique Plaid identifier for the Item","example":"M5eVJqLnv3tbzdngLDp9FL5OlDNxlNhlE55op"},"requestId":{"type":"string","description":"Request identifier for debugging and support","example":"Aim3b"}},"required":["financialAccountProviderId","plaidItemId","plaidExternalItemId","requestId"]},"ExchangePublicTokenDto":{"type":"object","properties":{"publicToken":{"type":"string","description":"Temporary token generated by Plaid Link (expires after 30 minutes)."}},"required":["publicToken"]},"AiChatImportJobEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"extractionCode":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2","nullable":true},"file":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"createdBy":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"platform":{"type":"string","enum":["claude","chatgpt","gemini"],"example":"claude"},"status":{"type":"string","enum":["not_started","in_progress","completed","failed"],"example":"completed"},"startedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"completedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:32:10.000Z","nullable":true},"failedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"message":{"type":"string","example":"Upload file not found","nullable":true},"aiChatProvider":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2","nullable":true},"stats":{"type":"object","example":{"conversations":12,"messages":348,"artifacts":0,"activities":0,"attachments":5},"nullable":true}},"required":["createdAt","updatedAt","id","case","extractionCode","file","createdBy","platform","status","startedAt","completedAt","failedAt","message","aiChatProvider","stats"]},"ImportAiChatDataDto":{"type":"object","properties":{"fileId":{"type":"string","description":"UserFile ID of the uploaded zip"},"platform":{"type":"string","enum":["claude","chatgpt","gemini"],"description":"AI platform: claude, chatgpt, or gemini"},"extractionCode":{"type":"string","description":"Extraction code string (optional)"}},"required":["fileId","platform"]},"LinkedInImportJobEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"extractionCode":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"file":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"createdBy":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"status":{"type":"string","enum":["not_started","in_progress","completed","failed"]},"startedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T14:25:00.000Z"},"completedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T14:30:00.000Z"},"failedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"message":{"type":"string","example":"Import failed","nullable":true},"linkedInUserProvider":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"selectedDataTypes":{"example":["messages","connections"],"type":"array","items":{"type":"string"}},"selectedConversationIds":{"description":"LinkedIn conversation ids the import was limited to; empty means all conversations.","example":["2-ZGVmYXVsdA=="],"type":"array","items":{"type":"string"}},"stats":{"type":"object","example":{"conversations":14,"messages":320,"connections":250,"comments":12,"richMedia":3,"savedItems":5,"savedJobAlerts":1,"savedJobs":2,"jobApplications":4}}},"required":["createdAt","updatedAt","id","case","extractionCode","file","createdBy","status","startedAt","completedAt","failedAt","message","linkedInUserProvider","selectedDataTypes","selectedConversationIds","stats"]},"LinkedInImportSkippedResponseEntity":{"type":"object","properties":{"imported":{"type":"boolean","enum":[false],"description":"Always false for a no-op"},"message":{"type":"string"}},"required":["imported","message"]},"ImportLinkedInDataExtractionDto":{"type":"object","properties":{"fileId":{"type":"string","description":"S3 file key of the uploaded zip"},"extractionCode":{"type":"string","description":"Extraction code string"},"selectedDataTypes":{"description":"Selected data types to import","type":"array","items":{"type":"string"}},"selectedConversationIds":{"description":"Specific conversation IDs to import","type":"array","items":{"type":"string"}}},"required":["fileId"]},"FetchClientLogDto":{"type":"object","properties":{"clientId":{"type":"string","description":"The client ID","example":"64a7672e5462512345678901"},"logType":{"type":"string","description":"The log type","example":"app_log","enum":["app_log","device_log"]},"deviceId":{"type":"string","description":"The device ID","example":"64a7672e5462512345678901"},"deviceType":{"type":"string","description":"The device type","example":"ios | android"}},"required":["clientId","logType"]},"SendIntercomSignalDto":{"type":"object","properties":{"extractionCode":{"type":"string","description":"The extraction code"},"type":{"type":"string","description":"The log type","example":"open_conversation","enum":["open_conversation"]},"conversationId":{"type":"string","description":"The conversaton ID","example":"64a7672e5462512345678901"}},"required":["extractionCode","type"]},"CreateOAuthUrlResponseEntity":{"type":"object","properties":{"url":{"type":"string","example":"https://accounts.example.com/oauth/authorize?client_id=abc123&state=xyz"}},"required":["url"]},"CreateOAuthUrlDto":{"type":"object","properties":{"redirectUri":{"type":"string"},"provider":{"type":"string","enum":["google","yahoo","imap","microsoft","icloud","ews"]}},"required":["redirectUri","provider"]},"CreateEmailProviderDto":{"type":"object","properties":{"redirectUri":{"type":"string"},"code":{"type":"string"},"extractionCode":{"type":"string"}},"required":["redirectUri","code","extractionCode"]},"CreateImapEmailProviderDto":{"type":"object","properties":{"extractionCode":{"type":"string"},"host":{"type":"string"},"port":{"type":"number"},"user":{"type":"string"},"pass":{"type":"string"}},"required":["extractionCode","host","port","user","pass"]},"SingleRemoteEmailEntity":{"type":"object","properties":{"starred":{"type":"boolean"},"unread":{"type":"boolean"},"folders":{"type":"array","items":{"type":"string"}},"subject":{"type":"string"},"threadId":{"type":"string"},"body":{"type":"string"},"grantId":{"type":"string"},"id":{"type":"string"},"object":{"type":"string"},"snippet":{"type":"string"},"bcc":{"type":"array","items":{"type":"object"}},"cc":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array","items":{"type":"object"}},"from":{"type":"array","items":{"type":"string"}},"to":{"type":"array","items":{"type":"string"}},"replyTo":{"type":"array","items":{"type":"string"}},"date":{"format":"date-time","type":"string"},"trackingOptions":{"type":"object"},"hasAttachment":{"type":"boolean"},"isDraft":{"type":"boolean"}},"required":["starred","unread","folders","subject","threadId","body","grantId","id","object","snippet","bcc","cc","attachments","from","to","replyTo","date","trackingOptions","hasAttachment","isDraft"]},"RemoteEmailSearchResponseEntity":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SingleRemoteEmailEntity"}},"nextPageToken":{"type":"string"}},"required":["data","nextPageToken"]},"SearchRemoteEmailsDto":{"type":"object","properties":{"anyEmail":{"type":"array","items":{"type":"string"}},"to":{"type":"array","items":{"type":"string"}},"bcc":{"type":"array","items":{"type":"string"}},"cc":{"type":"array","items":{"type":"string"}},"from":{"type":"array","items":{"type":"string"}},"hasAttachment":{"type":"boolean"},"receivedAfter":{"type":"string"},"receivedBefore":{"type":"string"},"keywords":{"type":"array","items":{"type":"string"}},"subject":{"type":"string"},"contactNames":{"type":"array","items":{"type":"string"}},"folders":{"description":"Restrict the search to these folders (folder id/path as returned by fetch-folders). Optional; when omitted, all non-system folders are searched.","type":"array","items":{"type":"string"}},"providerId":{"type":"string"},"pageToken":{"type":"string"},"limit":{"type":"number"}},"required":["providerId"]},"SingleRemoteEmailThreadEntity":{"type":"object","properties":{"grantId":{"type":"string"},"id":{"type":"string"},"object":{"type":"string"},"latestDraftOrMessage":{"$ref":"#/components/schemas/SingleRemoteEmailEntity"},"hasAttachments":{"type":"boolean"},"hasDrafts":{"type":"boolean"},"earliestMessageDate":{"format":"date-time","type":"string"},"latestMessageReceivedDate":{"format":"date-time","type":"string"},"latestMessageSentDate":{"format":"date-time","type":"string"},"folders":{"type":"array","items":{"type":"string"}},"participants":{"type":"array","items":{"type":"string"}},"snippet":{"type":"string"},"subject":{"type":"string"},"lastImportedAt":{"format":"date-time","type":"string","nullable":true,"description":"When this thread (any of its messages) was first imported into the case. Null when none of the thread messages have been imported."},"importedMessageIds":{"description":"Subset of the thread message ids that have already been imported.","type":"array","items":{"type":"string"}},"isDraft":{"type":"boolean"},"importedMessageCount":{"type":"number"},"isImported":{"type":"boolean"}},"required":["grantId","id","object","latestDraftOrMessage","hasAttachments","hasDrafts","earliestMessageDate","latestMessageReceivedDate","latestMessageSentDate","folders","participants","snippet","subject","importedMessageIds","isDraft","importedMessageCount","isImported"]},"RemoteEmailThreadSearchResponseEntity":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SingleRemoteEmailThreadEntity"}},"nextPageToken":{"type":"string"}},"required":["data","nextPageToken"]},"UserEmailImportJobEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"emailDataProvider":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"extractionCode":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"case":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"notificationEmails":{"example":["jane.doe@example.com"],"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["not_started","in_progress","completed","failed"]},"type":{"type":"string","enum":["by_thread","by_search","import_all","by_multi_query"]},"startedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"completedAt":{"format":"date-time","type":"string","example":null,"nullable":true},"failedAt":{"format":"date-time","type":"string","example":null,"nullable":true},"message":{"type":"string","example":"Importing threads","nullable":true},"jobData":{"type":"object","example":{"providerId":"65a1b2c3d4e5f6a7b8c9d0e1"},"nullable":true},"importedThreadsCount":{"type":"number","example":120},"importedEmailsCount":{"type":"number","example":348},"totalThreadsCount":{"type":"number","example":400},"totalEmailsCount":{"type":"number","example":1150},"isCancelled":{"type":"boolean","example":false},"scheduledFor":{"format":"date-time","type":"string","nullable":true,"example":null}},"required":["createdAt","updatedAt","id","emailDataProvider","extractionCode","case","notificationEmails","status","type","startedAt","completedAt","failedAt","message","jobData","importedThreadsCount","importedEmailsCount","totalThreadsCount","totalEmailsCount","isCancelled"]},"StartImportEmailSingleDto":{"type":"object","properties":{"threadId":{"type":"string"}},"required":["threadId"]},"SearchRemoteEmailsDtoBase":{"type":"object","properties":{"anyEmail":{"type":"array","items":{"type":"string"}},"to":{"type":"array","items":{"type":"string"}},"bcc":{"type":"array","items":{"type":"string"}},"cc":{"type":"array","items":{"type":"string"}},"from":{"type":"array","items":{"type":"string"}},"hasAttachment":{"type":"boolean"},"receivedAfter":{"type":"string"},"receivedBefore":{"type":"string"},"keywords":{"type":"array","items":{"type":"string"}},"subject":{"type":"string"},"contactNames":{"type":"array","items":{"type":"string"}},"folders":{"description":"Restrict the search to these folders (folder id/path as returned by fetch-folders). Optional; when omitted, all non-system folders are searched.","type":"array","items":{"type":"string"}}}},"MultiQueryImportDto":{"type":"object","properties":{"queries":{"description":"Raw Gmail boolean query strings (e.g. \"a OR b OR c\", \"x AND (y OR z)\"). Passed verbatim to Gmail q=, with date range appended.","type":"array","items":{"type":"string"}},"emails":{"description":"Email addresses to search for as any participant. Each address is expanded into (from:e OR to:e OR cc:e OR bcc:e) and run as its own query.","type":"array","items":{"type":"string"}},"dateStart":{"type":"string","description":"Optional inclusive lower bound of received date for all queries (ISO date). Omit for no lower bound."},"dateEnd":{"type":"string","description":"Optional inclusive upper bound of received date for all queries (ISO date). Omit for no upper bound."}}},"StartImportEmailsDto":{"type":"object","properties":{"providerId":{"type":"string"},"extractionCode":{"type":"string"},"notificationEmails":{"description":"Additional email addresses to send email to when export completes (other than user email)","type":"array","items":{"type":"string"}},"type":{"type":"string","enum":["by_thread","by_search","import_all","by_multi_query"],"default":"by_thread"},"emails":{"type":"array","items":{"$ref":"#/components/schemas/StartImportEmailSingleDto"}},"searchData":{"$ref":"#/components/schemas/SearchRemoteEmailsDtoBase"},"multiQueryData":{"$ref":"#/components/schemas/MultiQueryImportDto"},"folderIds":{"description":"Restrict the import to these provider folders (folder `id` as returned by fetch-folders). Optional. Applies to import_all, by_search and by_multi_query; ignored for by_thread.","type":"array","items":{"type":"string"}},"importAllSkipPages":{"type":"number","description":"Number of pages to skip for import all"},"scheduledFor":{"type":"string","description":"If set, schedule the import to start at this time instead of immediately. Must be in the future."}},"required":["providerId","extractionCode"]},"TagCountEntity":{"type":"object","properties":{"tag":{"type":"string","description":"Tag name.","example":"Responsive"},"count":{"type":"number","description":"Number of emails carrying this tag.","example":64}},"required":["tag","count"]},"EmailTagsCountResponseEntity":{"type":"object","properties":{"total":{"type":"number","example":42},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20},"totalPages":{"type":"number","example":3},"nextPage":{"type":"number","example":2,"nullable":true},"previousPage":{"type":"number","example":null,"nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/TagCountEntity"}}},"required":["total","totalPages","nextPage","previousPage","data"]},"QuerySearchCountEntity":{"type":"object","properties":{"query":{"type":"string","description":"The query string, echoed back.","example":"agreement AND signed"},"count":{"type":"number","description":"Number of emails matching the query.","example":17},"error":{"type":"string","description":"Set when the query could not be counted (parse error or search failure). When present, `count` is 0 and is not meaningful.","example":"search_failed"}},"required":["query","count"]},"EmailSearchCountEntity":{"type":"object","properties":{"email":{"type":"string","description":"The email address, echoed back.","example":"john.roe@example.org"},"count":{"type":"number","description":"Number of emails where this address is a participant (from/to/cc/bcc).","example":42}},"required":["email","count"]},"EmailSearchCountsResponseEntity":{"type":"object","properties":{"queries":{"type":"array","items":{"$ref":"#/components/schemas/QuerySearchCountEntity"}},"emails":{"type":"array","items":{"$ref":"#/components/schemas/EmailSearchCountEntity"}}},"required":["queries","emails"]},"EmailSearchCountsDto":{"type":"object","properties":{"queries":{"description":"Boolean query strings. Each is counted independently against the indexed email fields (subject, body, snippet, from/to/cc/bcc). Supports the same AND / OR / NOT / -prefix, parentheses, \"quoted phrases\" and wildcards as the boolean query parser.","example":["\"confidentiality agreement\"","(Nigeria OR Cyprus) AND NDA"],"type":"array","items":{"type":"string"}},"emails":{"description":"Email addresses. Each is counted as the number of emails where the address appears as any participant (from/to/cc/bcc), case-insensitive.","type":"array","items":{"type":"string"}},"providerId":{"type":"string","description":"Optional: narrow the counts to a single email provider (mailbox) within the case. When omitted, counts span the whole case."}}},"EmailDomainAddressEntity":{"type":"object","properties":{"email":{"type":"string","description":"A participant email address of the domain, lowercased.","example":"john.roe@example.org"},"count":{"type":"number","description":"Number of emails (documents) containing this address in from/to/cc/bcc.","example":42}},"required":["email","count"]},"EmailDomainAddressesResponseEntity":{"type":"object","properties":{"addresses":{"type":"array","items":{"$ref":"#/components/schemas/EmailDomainAddressEntity"}},"total":{"type":"number","description":"Distinct addresses in the domain (for pagination). Note: the sum of per-address counts may exceed the domain document count, since one email can contain multiple addresses of the same domain.","example":5},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20}},"required":["addresses","total","skip","limit"]},"BulkTagResponseEntity":{"type":"object","properties":{"mode":{"type":"string","enum":["by_id","by_search"]},"status":{"type":"string","description":"'completed' for by_id; 'queued' for by_search.","example":"completed"},"requested":{"type":"number","example":2},"matchedDocuments":{"type":"number","example":2},"skippedDocuments":{"type":"number","example":0},"resolvedTagIds":{"example":["65a1b2c3d4e5f6a7b8c9d0a1"],"type":"array","items":{"type":"string"}},"attachmentsCreated":{"type":"number","example":2},"emailsMatched":{"type":"number","description":"by_id threads + autoTagEmails","example":0},"emailAttachmentsCreated":{"type":"number","description":"by_id threads + autoTagEmails","example":0},"jobId":{"type":"string","description":"by_search job id","example":"65a1b2c3d4e5f6a7b8c9d0f2"}},"required":["mode","status"]},"SearchEmailsDto":{"type":"object","properties":{"page":{"type":"number","default":1},"perPage":{"type":"number","default":10},"skip":{"type":"number","description":"If skip/limit not provided, page/perPage will take precedence"},"limit":{"type":"number","description":"If skip/limit not provided, page/perPage will take precedence"},"sortOrder":{"type":"string","enum":["asc","desc"],"default":"asc"},"searchText":{"type":"string","description":"Text search applied to all indexed fields (subject, body, snippet, from/to/cc/bcc). Supports the same AND / OR / NOT / -prefix, parentheses, \"quoted phrases\" and wildcards as the boolean query parser.","example":"(discriminate OR racist) AND (\"Larry Graham\" OR Robinson)"},"folders":{"type":"array","items":{"type":"string"}},"domains":{"description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive. Requires the `domains` field on the emailtext Atlas index.","type":"array","items":{"type":"string"}},"excludeDomains":{"description":"Exclude items whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"excludeTags":{"description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","type":"array","items":{"type":"string"}},"untagged":{"type":"boolean","description":"When truthy (1/true), return only emails with no tags attached. Takes precedence over the `tags` filter."},"participants":{"description":"Filter by participant email address(es), any-of.","type":"array","items":{"type":"string"}},"excludeEmails":{"description":"Exclude items whose participant address matches any of these (any-of). Must not overlap `participants`.","type":"array","items":{"type":"string"}},"startDate":{"type":"string","format":"date-time","description":"Only return emails whose `date` is at/after this instant (inclusive). Accepts an ISO-8601 date-time.","example":"2024-01-01T00:00:00.000Z"},"endDate":{"type":"string","format":"date-time","description":"Only return emails whose `date` is at/before this instant (inclusive). Accepts an ISO-8601 date-time.","example":"2024-12-31T23:59:59.999Z"},"sourceEmails":{"type":"array","items":{"type":"string"}},"sortBy":{"type":"string","enum":["date","subject"],"default":"date"}}},"BulkTagEmailsDto":{"type":"object","properties":{"addMode":{"type":"string","enum":["by_id","by_search"]},"tags":{"minItems":1,"maxItems":100,"description":"Tag ids or names to ADD (additive union). Names created in the case if absent.","type":"array","items":{"type":"string"}},"ids":{"maxItems":500,"description":"Target ids (required when addMode=by_id).","type":"array","items":{"type":"string"}},"search":{"description":"Search query (required when addMode=by_search).","allOf":[{"$ref":"#/components/schemas/SearchEmailsDto"}]}},"required":["addMode","tags"]},"ListEmailThreadsDto":{"type":"object","properties":{"page":{"type":"number","default":1},"perPage":{"type":"number","default":10},"skip":{"type":"number","description":"If skip/limit not provided, page/perPage will take precedence"},"limit":{"type":"number","description":"If skip/limit not provided, page/perPage will take precedence"},"sortOrder":{"type":"string","enum":["asc","desc"],"default":"asc"},"folders":{"type":"array","items":{"type":"string"}},"domains":{"description":"Filter by participant domain(s) (part after @), any-of. Case-insensitive.","type":"array","items":{"type":"string"}},"excludeDomains":{"description":"Exclude items whose domain(s) match any of these (any-of). Case-insensitive. Must not overlap `domains`.","type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"excludeTags":{"description":"Exclude items carrying any of these tag ids. Must not overlap `tags`.","type":"array","items":{"type":"string"}},"participants":{"description":"Filter by participant email address(es), any-of.","type":"array","items":{"type":"string"}},"excludeEmails":{"description":"Exclude items whose participant address matches any of these (any-of). Must not overlap `participants`.","type":"array","items":{"type":"string"}},"starred":{"type":"boolean"},"unread":{"type":"boolean"},"untagged":{"type":"boolean","description":"When truthy (1/true), return only threads with no tags attached. Takes precedence over the `tags` filter."},"startDate":{"type":"string","format":"date-time","description":"Only return threads whose `latestMessageReceivedDate` is at/after this instant (inclusive). Accepts an ISO-8601 date-time.","example":"2024-01-01T00:00:00.000Z"},"endDate":{"type":"string","format":"date-time","description":"Only return threads whose `latestMessageReceivedDate` is at/before this instant (inclusive). Accepts an ISO-8601 date-time.","example":"2024-12-31T23:59:59.999Z"},"sourceEmails":{"type":"array","items":{"type":"string"}},"sortBy":{"type":"string","enum":["subject","latestMessageSentDate","latestMessageReceivedDate"],"default":"latestMessageReceivedDate"},"includeLastMessage":{"type":"boolean","description":"When truthy (1/true), populate lastMessageOrDraft (id/subject); otherwise it is returned as null. The message body is never included — use the thread `snippet` for previews, or fetch the body from the thread-emails endpoint."},"includeHidden":{"type":"boolean","default":false,"description":"When true, include hidden threads (hiddenAt != null). Default false: hidden threads are excluded from the listing."},"sharedStatus":{"type":"string","enum":["shared","pending","not_gated"],"description":"Opposing-council only. Narrows to threads in one sharing state; ignored for OL/OG callers."}}},"BulkTagThreadsDto":{"type":"object","properties":{"addMode":{"type":"string","enum":["by_id","by_search"]},"tags":{"minItems":1,"maxItems":100,"description":"Tag ids or names to ADD (additive union).","type":"array","items":{"type":"string"}},"ids":{"maxItems":500,"type":"array","items":{"type":"string"}},"search":{"$ref":"#/components/schemas/ListEmailThreadsDto"},"autoTagEmails":{"type":"boolean","default":false,"description":"When true, additively tag every email of the affected thread(s)."}},"required":["addMode","tags"]},"BulkThreadVisibilityResponseEntity":{"type":"object","properties":{"action":{"type":"string","enum":["hide","unhide"]},"requested":{"type":"number","description":"Number of ids in the request.","example":3},"matched":{"type":"number","description":"Ids that belong to the case.","example":3},"skipped":{"type":"number","description":"Ids skipped (not in the case).","example":0},"modified":{"type":"number","description":"Documents whose hiddenAt actually changed.","example":3}},"required":["action","requested","matched","skipped","modified"]},"BulkThreadVisibilityDto":{"type":"object","properties":{"action":{"type":"string","enum":["hide","unhide"]},"ids":{"minItems":1,"maxItems":1000,"description":"Thread ids to hide/unhide. Ids not in the case are skipped.","type":"array","items":{"type":"string"}}},"required":["action","ids"]},"BulkTagJobEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f2"},"hostType":{"type":"string","enum":["emails","threads"]},"status":{"type":"string","enum":["queued","in_progress","completed","failed"]},"matchedDocuments":{"type":"number","example":250},"attachmentsCreated":{"type":"number","example":250},"autoTagEmails":{"type":"boolean","example":false},"emailsMatched":{"type":"number","example":0},"emailAttachmentsCreated":{"type":"number","example":0},"resolvedTagIds":{"example":["65a1b2c3d4e5f6a7b8c9d0a1"],"type":"array","items":{"type":"string"}},"message":{"type":"string","nullable":true,"example":null},"startedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T14:30:05.000Z"},"completedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T14:30:41.000Z"},"failedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"}},"required":["id","hostType","status","matchedDocuments","attachmentsCreated","autoTagEmails","emailsMatched","emailAttachmentsCreated","resolvedTagIds","message","startedAt","completedAt","failedAt","createdAt"]},"UpdateUserEmailDataTagsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Id of the case the email belongs to. Required so @CaseAccess can authorize the caller (an OC included) on the case."},"tags":{"minItems":0,"maxItems":100,"description":"Array of tag ids or tag names. Replaces the email's tags with this list. Pass an empty array to remove all tags.","type":"array","items":{"type":"string"}}},"required":["caseId","tags"]},"UpdateEmailThreadTagsDto":{"type":"object","properties":{"tags":{"minItems":0,"maxItems":100,"description":"Tag ids or names. Replaces the thread tags with this list. Empty array removes all thread tags.","type":"array","items":{"type":"string"}},"autoTagEmails":{"type":"boolean","default":false,"description":"When true, additively tag every email of this thread (emails are never removed from; empty tags leaves emails untouched)."}},"required":["tags"]},"DeleteEmailThreadsDto":{"type":"object","properties":{"threadIds":{"minItems":1,"maxItems":1000,"description":"Array of email thread mongo ids to delete.","type":"array","items":{"type":"string"}}},"required":["threadIds"]},"DiscordConversationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"channelId":{"type":"string","example":"1197400000000000001"},"type":{"type":"string","example":"1","nullable":true},"recipients":{"example":["1197412345678901234","1197498765432109876"],"type":"array","items":{"type":"string"}},"name":{"type":"string","example":"c1197400000000000001","nullable":true},"displayName":{"type":"string","description":"Not filled by any importer today, so always null.","nullable":true,"example":null},"messagesCount":{"type":"number","example":42},"firstMessageDate":{"format":"date-time","type":"string","example":"2026-01-12T09:00:00.000Z","nullable":true},"lastMessageDate":{"format":"date-time","type":"string","example":"2026-01-15T18:02:00.000Z","nullable":true},"lastMessageText":{"type":"string","example":"See you at 6.","nullable":true}},"required":["createdAt","updatedAt","id","channelId","type","recipients","name","displayName","messagesCount","firstMessageDate","lastMessageDate","lastMessageText"]},"DiscordMessageEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"discordConversation":{"type":"string","example":"507f1f77bcf86cd799439011"},"discordUserProvider":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"externalId":{"type":"string","example":"1197412345678901234"},"text":{"type":"string","example":"See you at 6.","nullable":true},"date":{"format":"date-time","type":"string","example":"2026-01-15T18:02:00.000Z","nullable":true},"attachments":{"type":"string","example":"https://cdn.discordapp.com/attachments/1197400000000000000/1197412345678901235/photo.png","nullable":true},"metadata":{"type":"object","description":"Not filled by any importer today, so always null.","example":null,"nullable":true}},"required":["createdAt","updatedAt","id","discordConversation","discordUserProvider","externalId","text","date","attachments","metadata"]},"DiscordImportJobEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"extractionCode":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"file":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"createdBy":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"status":{"type":"string","enum":["not_started","in_progress","completed","failed"]},"startedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:25:00.000Z","nullable":true},"completedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"failedAt":{"format":"date-time","type":"string","example":null,"nullable":true},"message":{"type":"string","example":"Discord export is missing the messages folder","nullable":true},"discordUserProvider":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"stats":{"type":"object","example":{"conversations":12,"messages":3480,"activities":250,"tickets":1},"nullable":true}},"required":["createdAt","updatedAt","id","case","extractionCode","file","createdBy","status","startedAt","completedAt","failedAt","message","discordUserProvider","stats"]},"DiscordActivityEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"eventType":{"type":"string","example":"app_opened","nullable":true},"eventId":{"type":"string","example":"AQEAAKzM7VxNfC0eEHn1example"},"eventSource":{"type":"string","example":"analytics","nullable":true},"timestamp":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"rawData":{"type":"object","example":{"event_type":"app_opened","event_id":"AQEAAKzM7VxNfC0eEHn1example","timestamp":"\"2026-01-15T14:30:00.000Z\"","os":"Windows","client_version":"1.0.9032"},"nullable":true}},"required":["createdAt","updatedAt","id","eventType","eventId","eventSource","timestamp","rawData"]},"DiscordSupportTicketEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"discordUserProvider":{"type":"string","example":"507f1f77bcf86cd799439011","description":"Id of the Discord profile the ticket belongs to"},"ticketId":{"type":"number","example":41827364},"subject":{"type":"string","example":"Unable to log in to my account","nullable":true},"status":{"type":"string","example":"solved","nullable":true},"ticketCreatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"comments":{"type":"array","items":{"type":"object"},"example":[{"author":"Discord Support","comment":"Thanks for reaching out, we have reset your two-factor settings.","createdAt":"2026-01-15T14:30:00.000Z"}]}},"required":["createdAt","updatedAt","id","discordUserProvider","ticketId","subject","status","ticketCreatedAt","comments"]},"DeleteDiscordProfileDto":{"type":"object","properties":{"discordProviderId":{"type":"string","description":"DiscordUserProvider ID"}},"required":["discordProviderId"]},"ImportDiscordDataDto":{"type":"object","properties":{"fileId":{"type":"string","description":"UserFile ID of the uploaded zip"},"extractionCode":{"type":"string","description":"Extraction code string (optional)"}},"required":["fileId"]},"RedditPostEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"externalId":{"type":"string","example":"1abc2de"},"permalink":{"type":"string","example":"https://www.reddit.com/r/personalfinance/comments/1abc2de/how_should_i_split_rent_with_a_roommate/","nullable":true},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"ip":{"type":"string","example":"203.0.113.7","nullable":true},"subreddit":{"type":"string","example":"personalfinance","nullable":true},"gildings":{"type":"number","example":0},"title":{"type":"string","example":"How should I split rent with a roommate?","nullable":true},"url":{"type":"string","example":"https://www.reddit.com/r/personalfinance/comments/1abc2de/how_should_i_split_rent_with_a_roommate/","nullable":true},"body":{"type":"string","example":"We moved in together last month and disagree on how to split it.","nullable":true}},"required":["createdAt","updatedAt","id","externalId","permalink","date","ip","subreddit","gildings","title","url","body"]},"RedditCommentEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"externalId":{"type":"string","example":"k9x2abc"},"permalink":{"type":"string","example":"https://www.reddit.com/r/legaladvice/comments/17abcde/question_about_a_lease/k9x2abc/","nullable":true},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"ip":{"type":"string","example":"203.0.113.42","nullable":true},"subreddit":{"type":"string","example":"legaladvice","nullable":true},"gildings":{"type":"number","example":0},"link":{"type":"string","example":"https://www.reddit.com/r/legaladvice/comments/17abcde/question_about_a_lease/","nullable":true},"parent":{"type":"string","example":"k9x1zzz","nullable":true},"body":{"type":"string","example":"Check clause 4 before you sign.","nullable":true},"media":{"type":"string","nullable":true,"example":null}},"required":["createdAt","updatedAt","id","externalId","permalink","date","ip","subreddit","gildings","link","parent","body","media"]},"RedditMessageEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"externalId":{"type":"string","example":"t4_2a1b3c"},"permalink":{"type":"string","example":"https://www.reddit.com/message/messages/2a1b3c","nullable":true},"threadId":{"type":"string","example":"t4_2a1b3c","nullable":true},"date":{"format":"date-time","type":"string","example":"2026-01-15T18:02:00.000Z","nullable":true},"ip":{"type":"string","example":"203.0.113.42","nullable":true},"from":{"type":"string","example":"example_user","nullable":true},"to":{"type":"string","example":"another_user","nullable":true},"subject":{"type":"string","example":"Re: the listing","nullable":true},"body":{"type":"string","example":"Thanks for the tip.","nullable":true}},"required":["createdAt","updatedAt","id","externalId","permalink","threadId","date","ip","from","to","subject","body"]},"RedditChatMessageEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"externalId":{"type":"string","example":"2961408275"},"redditCreatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"redditUpdatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"username":{"type":"string","example":"jane_doe_42","nullable":true},"message":{"type":"string","example":"Is the apartment still available?","nullable":true},"threadParentMessageId":{"type":"string","example":"2961408100","nullable":true},"channelUrl":{"type":"string","example":"sendbird_group_channel_2073012_5a1b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b","nullable":true},"subreddit":{"type":"string","example":"legaladvice","nullable":true},"channelName":{"type":"string","example":"jane_doe_42, john_smith_7","nullable":true},"conversationType":{"type":"string","example":"direct","nullable":true}},"required":["createdAt","updatedAt","id","externalId","redditCreatedAt","redditUpdatedAt","username","message","threadParentMessageId","channelUrl","subreddit","channelName","conversationType"]},"RedditVoteEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"externalId":{"type":"string","example":"17abcde"},"permalink":{"type":"string","example":"https://www.reddit.com/r/legaladvice/comments/17abcde/question_about_a_lease/","nullable":true},"direction":{"type":"string","example":"up","nullable":true},"voteType":{"type":"string","example":"post"}},"required":["createdAt","updatedAt","id","externalId","permalink","direction","voteType"]},"RedditImportJobEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"case":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"extractionCode":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"file":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"createdBy":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"redditUserProvider":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"status":{"type":"string","enum":["not_started","in_progress","completed","failed"]},"startedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T14:25:00.000Z"},"completedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T14:30:00.000Z"},"failedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"message":{"type":"string","example":"Import failed","nullable":true},"stats":{"type":"object","example":{"posts":42,"comments":310,"messages":18,"chatMessages":95,"votes":1200},"nullable":true}},"required":["createdAt","updatedAt","id","case","extractionCode","file","createdBy","redditUserProvider","status","startedAt","completedAt","failedAt","message","stats"]},"DeleteRedditProfileDto":{"type":"object","properties":{"redditProviderId":{"type":"string","description":"RedditUserProvider ID"}},"required":["redditProviderId"]},"ImportRedditDataDto":{"type":"object","properties":{"fileId":{"type":"string","description":"UserFile ID of the uploaded zip"},"extractionCode":{"type":"string","description":"Extraction code string (optional)"}},"required":["fileId"]},"LinkedInUserProviderEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"linkedInProfileUrl":{"type":"string","example":"linkedin-export-jane-doe"},"firstName":{"type":"string","example":"Jane","nullable":true},"lastName":{"type":"string","example":"Doe","nullable":true},"headline":{"type":"string","example":"Operations Manager at Acme Corp","nullable":true},"industry":{"type":"string","example":"Logistics and Supply Chain","nullable":true},"email":{"type":"string","example":"jane.doe@example.com","nullable":true},"summary":{"type":"string","example":"Operations leader with ten years in regional logistics.","nullable":true},"geoLocation":{"type":"string","example":"Springfield, Illinois, United States","nullable":true},"avatar":{"type":"object","nullable":true,"example":null,"description":"Not loaded by any route, so always null."},"metadata":{"type":"object","example":{"address":"123 Main St, Springfield, IL","zipCode":"62701","websites":"[PERSONAL:https://example.com]"},"nullable":true},"deletedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"deletedBy":{"type":"string","nullable":true,"example":null},"ocSharedStatus":{"type":"string","nullable":true,"enum":["shared","pending","not_gated"],"description":"Opposing-council only: whether this provider has been shared with the OL. `null` for OL/OG callers — same convention as every other item listing. Provider-level: every item under it shares this status, since approval here is all-or-nothing."}},"required":["createdAt","updatedAt","id","linkedInProfileUrl","firstName","lastName","headline","industry","email","summary","geoLocation","avatar","metadata","deletedAt","deletedBy"]},"LinkedInImportCoverageResponseEntity":{"type":"object","properties":{"fileId":{"type":"string","description":"UserFile ID the coverage is reported for"},"importedAllDataTypes":{"type":"boolean","description":"True when a prior job imported every data type"},"importedDataTypes":{"description":"Union of data types imported across completed jobs","type":"array","items":{"type":"string"}},"importedAllConversations":{"type":"boolean","description":"True when a prior job imported every conversation"},"importedConversationIds":{"description":"Union of conversation IDs imported across completed jobs","type":"array","items":{"type":"string"}}},"required":["fileId","importedAllDataTypes","importedDataTypes","importedAllConversations","importedConversationIds"]},"ImportLinkedInDataDto":{"type":"object","properties":{"fileId":{"type":"string","description":"UserFile ID of the uploaded zip"},"extractionCode":{"type":"string","description":"Extraction code string (optional)"},"selectedDataTypes":{"description":"Selected data types to import (optional)","type":"array","items":{"type":"string"}},"selectedConversationIds":{"description":"Specific conversation IDs to import (omit to import all)","type":"array","items":{"type":"string"}}},"required":["fileId"]},"ExportLinkedInDataDto":{"type":"object","properties":{"linkedInExportType":{"type":"string","description":"LinkedIn data type to export.","enum":["conversations","connections","job-applications","rich-media","saved-jobs","saved-job-alerts","saved-items"]},"sortBy":{"type":"string","description":"Sort order for exported records.","enum":["old-to-new","new-to-old"]},"tags":{"description":"Filter by tag IDs. Only exports records that have all of the provided tags.","type":"array","items":{"type":"string"}},"exportConfig":{"type":"array","description":"Export configuration flags (e.g. include tags in output).","items":{"type":"string","enum":["phone-numbers","comments","tags","app-icons","device-name","images","hide-attachment-links","extra-texts","line-break","device-logs","custom-entry","recently-deleted","deleted-messages-inline","deleted-messages-page","deleted-messages-inline-page","ignore-conversations","search-history-page","hide-page-numbers","print-footer-work-timestamp"]}},"custodianName":{"type":"string","description":"Custodian name to display on the cover page."},"conversationIds":{"description":"Specific conversation IDs to export. Only used when linkedInExportType is \"conversations\". When omitted, all conversations are exported.","type":"array","items":{"type":"string"}}},"required":["linkedInExportType"]},"LinkedInMessageSearchResultEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"from":{"type":"string","example":"Jane Doe"},"to":{"type":"string","example":"John Smith"},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"content":{"type":"string","example":"Thanks for connecting. Are you free for a call next week?"},"folder":{"type":"string","nullable":true,"example":"INBOX"},"isDraft":{"type":"boolean","example":false},"attachments":{"type":"string","example":""},"conversationId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"conversationTitle":{"type":"string","nullable":true,"example":"Project update"},"participants":{"example":["Jane Doe","John Smith"],"type":"array","items":{"type":"string"}},"profileId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"}},"required":["id","from","to","date","content","folder","isDraft","attachments","conversationId","conversationTitle","participants","profileId"]},"LinkedInConversationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"conversationId":{"type":"string","example":"2-ZGVmYXVsdA=="},"title":{"type":"string","example":"Introduction","nullable":true},"participants":{"type":"array","items":{"type":"object"},"example":[{"name":"Jane Doe","profileUrl":"https://www.linkedin.com/in/jane-doe"}]},"messagesCount":{"type":"number","example":4},"firstMessageDate":{"format":"date-time","type":"string","example":"2026-01-12T09:00:00.000Z","nullable":true},"lastMessageDate":{"format":"date-time","type":"string","example":"2026-01-15T09:12:00.000Z","nullable":true},"lastMessageText":{"type":"string","example":"Happy to connect.","nullable":true},"tags":{"example":["507f1f77bcf86cd799439011"],"type":"array","items":{"type":"string"}},"commentsCount":{"type":"number","example":0},"comments":{"type":"array","description":"Comment references on the conversation","items":{"type":"object"},"example":[{"_id":"65a1b2c3d4e5f6a7b8c9d0e3","case":"507f1f77bcf86cd799439011","comment":"65a1b2c3d4e5f6a7b8c9d0e2"}]},"starredAt":{"format":"date-time","type":"string","nullable":true,"example":null},"starredBy":{"type":"string","nullable":true,"example":null},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["createdAt","updatedAt","id","conversationId","title","participants","messagesCount","firstMessageDate","lastMessageDate","lastMessageText","tags","commentsCount","comments","starredAt","starredBy"]},"LinkedInMessageEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"from":{"type":"string","example":"Jane Doe","nullable":true},"senderProfileUrl":{"type":"string","example":"https://www.linkedin.com/in/jane-doe-example","nullable":true},"to":{"type":"string","example":"John Roe","nullable":true},"recipientProfileUrls":{"example":["https://www.linkedin.com/in/john-roe-example"],"type":"array","items":{"type":"string"}},"date":{"format":"date-time","type":"string","example":"2026-01-15T09:12:00.000Z"},"subject":{"type":"string","example":"Introduction","nullable":true},"content":{"type":"string","example":"Happy to connect.","nullable":true},"folder":{"type":"string","example":"INBOX","nullable":true},"attachments":{"type":"string","example":"https://www.linkedin.com/dms/prv/attachment/v2/D4E06AQExample/messaging-attachmentFile/0/1700000000000","nullable":true},"isDraft":{"type":"boolean","example":false}},"required":["createdAt","updatedAt","id","from","senderProfileUrl","to","recipientProfileUrls","date","subject","content","folder","attachments","isDraft"]},"LinkedInConnectionEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"firstName":{"type":"string","example":"Jane","nullable":true},"lastName":{"type":"string","example":"Doe","nullable":true},"profileUrl":{"type":"string","example":"https://www.linkedin.com/in/jane-doe-example","nullable":true},"email":{"type":"string","example":null,"nullable":true},"company":{"type":"string","example":"Example Corp","nullable":true},"position":{"type":"string","example":"Paralegal","nullable":true},"connectedOn":{"format":"date-time","type":"string","example":"2025-11-03T00:00:00.000Z","nullable":true},"tags":{"example":["65a1b2c3d4e5f6a7b8c9d0e2"],"type":"array","items":{"type":"string"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["createdAt","updatedAt","id","firstName","lastName","profileUrl","email","company","position","connectedOn","tags"]},"LinkedInCommentEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"postLink":{"type":"string","example":"https://www.linkedin.com/feed/update/urn:li:activity:7012345678901234567"},"message":{"type":"string","example":"Congratulations on the new role!","nullable":true},"tags":{"example":["65a1b2c3d4e5f6a7b8c9d0e2"],"type":"array","items":{"type":"string"}},"attachedTags":{"type":"array","description":"Tag records exactly as stored: they include removed tags (removedAt set) and tags from both sides of the case, and carry no tag name.","items":{"type":"object","properties":{"tagId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"addedBy":{"type":"string","enum":["user","ai","auto","auto-import","system","default"]},"addedById":{"type":"string","nullable":true,"example":"507f1f77bcf86cd799439011"},"addedAt":{"type":"string","format":"date-time","example":"2026-01-15T14:30:00.000Z"},"removedAt":{"type":"string","format":"date-time","nullable":true,"example":null},"removedBy":{"type":"string","enum":["user","ai","auto","auto-import","system","default"],"nullable":true,"example":null},"removedById":{"type":"string","nullable":true,"example":null}}},"example":[{"tagId":"65a1b2c3d4e5f6a7b8c9d0e2","addedBy":"user","addedById":"507f1f77bcf86cd799439011","addedAt":"2026-01-15T14:30:00.000Z","removedAt":null,"removedBy":null,"removedById":null}]}},"required":["createdAt","updatedAt","id","date","postLink","message","tags","attachedTags"]},"LinkedInRichMediaEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"dateTime":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"description":{"type":"string","example":"Profile photo","nullable":true},"mediaLink":{"type":"string","example":"https://media.licdn.com/dms/image/v2/D4E03AQExample/profile-displayphoto-shrink_800_800/0/1700000000000","nullable":true},"mediaType":{"type":"string","example":"photo","nullable":true},"tags":{"example":["65a1b2c3d4e5f6a7b8c9d0e2"],"type":"array","items":{"type":"string"}},"mediaFile":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e2"}},"required":["createdAt","updatedAt","id","dateTime","description","mediaLink","mediaType","tags"]},"LinkedInSavedJobEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"savedDate":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"jobUrl":{"type":"string","example":"https://www.linkedin.com/jobs/view/3812345678"},"jobTitle":{"type":"string","example":"Paralegal","nullable":true},"companyName":{"type":"string","example":"Acme Law","nullable":true},"tags":{"example":["507f1f77bcf86cd799439011"],"type":"array","items":{"type":"string"}}},"required":["createdAt","updatedAt","id","savedDate","jobUrl","jobTitle","companyName","tags"]},"LinkedInSavedItemEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"url":{"type":"string","example":"https://www.linkedin.com/feed/update/urn:li:activity:7151234567890123456"},"createdTime":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true}},"required":["createdAt","updatedAt","id","url","createdTime"]},"LinkedInJobApplicationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"applicationDate":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"contactEmail":{"type":"string","example":"jane.doe@example.com","nullable":true},"contactPhone":{"type":"string","example":"+15555550100","nullable":true},"companyName":{"type":"string","example":"Acme Corp","nullable":true},"jobTitle":{"type":"string","example":"Paralegal","nullable":true},"jobUrl":{"type":"string","example":"https://www.linkedin.com/jobs/view/3812345678","nullable":true},"resumeName":{"type":"string","example":"Jane_Doe_Resume.pdf","nullable":true},"questionsAndAnswers":{"type":"array","items":{"type":"object"},"example":[{"answer":"5","question":"How many years of work experience do you have?"}]},"tags":{"example":["65a1b2c3d4e5f6a7b8c9d0e2"],"type":"array","items":{"type":"string"}},"commentsCount":{"type":"number","example":1},"comments":{"type":"array","items":{"type":"object"},"example":[{"_id":"65a1b2c3d4e5f6a7b8c9d0e3","case":"507f1f77bcf86cd799439011","comment":"65a1b2c3d4e5f6a7b8c9d0e2"}]}},"required":["createdAt","updatedAt","id","applicationDate","contactEmail","contactPhone","companyName","jobTitle","jobUrl","resumeName","questionsAndAnswers","tags","commentsCount","comments"]},"LinkedInSavedJobAlertEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"savedSearchId":{"type":"string","example":"1234567890","nullable":true},"alertParameters":{"type":"string","example":"keywords:paralegal,location:Chicago, Illinois","nullable":true},"queryContext":{"type":"string","example":"JOB_SEARCH","nullable":true}},"required":["createdAt","updatedAt","id","savedSearchId","alertParameters","queryContext"]},"UpdateLinkedInTagsDto":{"type":"object","properties":{"tagId":{"type":"string","description":"Tag ID to toggle on the resource"}},"required":["tagId"]},"DeleteLinkedInProfileDto":{"type":"object","properties":{"action":{"type":"string","description":"Action to perform: \"archive\" or \"delete\""}},"required":["action"]},"AiChatMessageSearchResultEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"role":{"type":"string","enum":["user","assistant","system","tool"],"example":"user"},"text":{"type":"string","example":"Summarise clause 4 of the lease in plain English."},"highlightedText":{"type":"string","example":"Summarise [hl]clause 4[/hl] of the lease in plain English."},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"senderName":{"type":"string","nullable":true,"example":"Jane Doe"},"model":{"type":"string","nullable":true,"example":"gpt-4o"},"conversationId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"conversationTitle":{"type":"string","nullable":true,"example":"Lease review"},"providerId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e3"}},"required":["id","role","text","date","senderName","model","conversationId","conversationTitle","providerId"]},"AiChatConversationEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"aiChatProvider":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"externalId":{"type":"string","example":"conv_01HZX3K9P7"},"title":{"type":"string","example":"Lease review","nullable":true},"createdDate":{"format":"date-time","type":"string","example":"2026-01-15T14:28:00.000Z","nullable":true},"updatedDate":{"format":"date-time","type":"string","example":"2026-01-15T14:41:00.000Z","nullable":true},"model":{"type":"string","example":"gpt-4o","nullable":true},"projectId":{"type":"string","example":"0f8e2c1a-6b7d-4e3f-9a1b-2c3d4e5f6a7b","nullable":true},"projectName":{"type":"string","example":"Contract reviews","nullable":true},"systemPrompt":{"type":"string","example":"You are a helpful assistant.","nullable":true},"isShared":{"type":"boolean","description":"Not filled by any importer today, so always false.","example":false},"messagesCount":{"type":"number","example":12},"firstMessageDate":{"format":"date-time","type":"string","example":"2026-01-15T14:28:00.000Z","nullable":true},"lastMessageDate":{"format":"date-time","type":"string","example":"2026-01-15T14:41:00.000Z","nullable":true},"lastMessageText":{"type":"string","example":"Here is a plain-English summary of clause 4.","nullable":true},"totalInputTokens":{"type":"number","description":"Not filled by any importer today, so always 0.","example":0},"totalOutputTokens":{"type":"number","description":"Not filled by any importer today, so always 0.","example":0},"tags":{"type":"array","description":"Tags on the conversation, each with its name (legacy; attachedTags is the side-aware list).","items":{"type":"object","properties":{"_id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0a1"},"name":{"type":"string","example":"Responsive"}}},"example":[{"_id":"65a1b2c3d4e5f6a7b8c9d0a1","name":"Responsive"}]},"commentsCount":{"type":"number","example":2},"comments":{"type":"array","description":"Comment references on the conversation","items":{"type":"object"},"example":[{"_id":"65a1b2c3d4e5f6a7b8c9d0e3","case":"507f1f77bcf86cd799439011","comment":"65a1b2c3d4e5f6a7b8c9d0e2"}]},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["createdAt","updatedAt","id","aiChatProvider","externalId","title","createdDate","updatedDate","model","projectId","projectName","systemPrompt","isShared","messagesCount","firstMessageDate","lastMessageDate","lastMessageText","totalInputTokens","totalOutputTokens"]},"AiChatMessageEntity":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"aiChatConversation":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"aiChatProvider":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"externalId":{"type":"string","example":"msg_01HZX3K9Q2"},"role":{"type":"string","enum":["user","assistant","system","tool"],"example":"user"},"text":{"type":"string","example":"Summarise clause 4 of the lease in plain English.","nullable":true},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z","nullable":true},"senderName":{"type":"string","example":"Jane Doe","nullable":true},"senderId":{"type":"string","nullable":true,"example":null,"description":"Not filled by any importer today, so always null."},"model":{"type":"string","example":"gpt-4o","nullable":true},"inputTokens":{"type":"number","example":38},"outputTokens":{"type":"number","example":0},"toolUses":{"type":"array","items":{"type":"object"},"example":[{"toolName":"web_search","toolInput":{"query":"statute of limitations Illinois"},"toolOutput":null,"toolCallId":"toolu_01A2B3C4D5"}]},"attachments":{"type":"array","items":{"type":"object"},"example":[{"fileName":"contract-draft.pdf","fileType":"application/pdf","fileSize":48213,"externalUrl":null,"userFile":"507f1f77bcf86cd799439011"}]},"artifacts":{"example":["65a1b2c3d4e5f6a7b8c9d0e2"],"type":"array","items":{"type":"string"}},"highlightedText":{"type":"string","example":"Summarise [hl]clause 4[/hl] of the lease in plain English."}},"required":["createdAt","updatedAt","id","aiChatConversation","aiChatProvider","externalId","role","text","date","senderName","senderId","model","inputTokens","outputTokens","toolUses","attachments","artifacts"]},"DeleteAiChatProfileDto":{"type":"object","properties":{"aiChatProviderId":{"type":"string","description":"AiChatProvider ID to archive"}},"required":["aiChatProviderId"]},"UpdateAiChatConversationTagsDto":{"type":"object","properties":{"tags":{"description":"Array of tag names or tag IDs to apply","type":"array","items":{"type":"string"}}},"required":["tags"]},"AiBackfillRequestDto":{"type":"object","properties":{"caseIds":{"type":"array","items":{"type":"string"}},"organizationIds":{"type":"array","items":{"type":"string"}},"dryRun":{"type":"boolean","default":false},"confirmUnrestricted":{"type":"boolean","default":false},"force":{"type":"boolean","default":false}}},"AiCaseResetDto":{"type":"object","properties":{"dryRun":{"type":"boolean","default":false,"description":"When true, return the counts that would have been deleted without actually deleting anything."}}},"AiSearchExpandedNeighborEntity":{"type":"object","properties":{"messageId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f2"},"text":{"type":"string","example":"See you at the exchange on Friday."},"timestamp":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"}},"required":["messageId","text","timestamp"]},"AiSearchExpandedContextEntity":{"type":"object","properties":{"before":{"type":"array","items":{"$ref":"#/components/schemas/AiSearchExpandedNeighborEntity"}},"after":{"type":"array","items":{"$ref":"#/components/schemas/AiSearchExpandedNeighborEntity"}}},"required":["before","after"]},"AiSearchItemEntity":{"type":"object","properties":{"kind":{"type":"string","enum":["message","chunk"],"example":"message"},"id":{"type":"string","description":"Primary id of the item — DeviceMessage._id when kind=message, AiConversationChunk._id when kind=chunk","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"messageId":{"type":"string","description":"Back-compat alias for `id` — Phase 1 clients used this","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"source":{"type":"string","enum":["device_message","linkedin","reddit","discord","email","ai_chat"],"example":"device_message"},"conversationId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f0"},"sender":{"type":"string","example":"+15555550123"},"timestamp":{"format":"date-time","type":"string","example":"2026-01-15T10:15:00.000Z"},"score":{"type":"number","example":0.83},"snippet":{"type":"string","example":"I will not be at the pickup tomorrow."},"topics":{"description":"Topic tags — only present when kind=chunk","example":["custody","scheduling"],"type":"array","items":{"type":"string"}},"expandedContext":{"$ref":"#/components/schemas/AiSearchExpandedContextEntity"}},"required":["kind","id","messageId","source","conversationId","score","snippet"]},"AiSearchResponseEntity":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AiSearchItemEntity"}},"candidateCount":{"type":"number","example":50},"vectorSearchTopN":{"type":"number","example":50},"rerankApplied":{"type":"boolean","example":true},"queryEmbeddingModel":{"type":"string","nullable":true,"example":"cohere.embed-english-v3"}},"required":["items","candidateCount","vectorSearchTopN","rerankApplied"]},"AiSearchDateRangeDto":{"type":"object","properties":{"start":{"format":"date-time","type":"string"},"end":{"format":"date-time","type":"string"}}},"AiSearchFiltersDto":{"type":"object","properties":{"sources":{"type":"array","items":{"type":"string","enum":["device_message","linkedin","reddit","discord","email","ai_chat"]}},"dateRange":{"$ref":"#/components/schemas/AiSearchDateRangeDto"},"participants":{"type":"array","items":{"type":"string"}},"conversationIds":{"type":"array","items":{"type":"string"}},"topics":{"type":"array","items":{"type":"string"}},"legalRelevance":{"type":"array","items":{"type":"string"}}}},"AiSearchExpandContextDto":{"type":"object","properties":{"before":{"type":"number","default":0},"after":{"type":"number","default":0}}},"AiSearchDto":{"type":"object","properties":{"query":{"type":"string"},"filters":{"$ref":"#/components/schemas/AiSearchFiltersDto"},"granularity":{"type":"string","enum":["messages","chunks","mixed"],"default":"messages"},"topK":{"type":"number","default":50,"minimum":1,"maximum":100},"rerank":{"type":"boolean","default":true},"expandContext":{"$ref":"#/components/schemas/AiSearchExpandContextDto"}},"required":["query"]},"AiChunkingPhaseEntity":{"type":"object","properties":{"conversationsTotal":{"type":"number","example":40},"conversationsDone":{"type":"number","example":40},"percent":{"type":"number","description":"0–100","example":100},"startedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:15:00.000Z"},"finishedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:20:00.000Z"}},"required":["conversationsTotal","conversationsDone","percent"]},"AiEmbeddingPhaseEntity":{"type":"object","properties":{"messagesTotal":{"type":"number","example":12000},"messagesDone":{"type":"number","example":9000},"percent":{"type":"number","description":"0–100","example":75},"startedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:15:00.000Z"},"finishedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"ratePerSec":{"type":"number","nullable":true,"example":25},"etaSeconds":{"type":"number","nullable":true,"description":"Estimated seconds remaining for the embedding phase","example":120}},"required":["messagesTotal","messagesDone","percent"]},"AiSummarizationPhaseEntity":{"type":"object","properties":{"chunksTotal":{"type":"number","example":300},"chunksDone":{"type":"number","example":0},"percent":{"type":"number","description":"0–100","example":0},"startedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"finishedAt":{"format":"date-time","type":"string","nullable":true,"example":null}},"required":["chunksTotal","chunksDone","percent"]},"AiCaseProgressEntity":{"type":"object","properties":{"case":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"organization":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e3"},"source":{"type":"string","enum":["device_message","linkedin","reddit","discord","email","ai_chat"],"example":"device_message"},"status":{"type":"string","enum":["not_started","in_progress","ready","failed"],"example":"in_progress"},"chunking":{"$ref":"#/components/schemas/AiChunkingPhaseEntity"},"embedding":{"$ref":"#/components/schemas/AiEmbeddingPhaseEntity"},"summarization":{"$ref":"#/components/schemas/AiSummarizationPhaseEntity"},"overallPercent":{"type":"number","description":"Combined progress across the running phases (0–100)","example":58},"startedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:15:00.000Z"},"lastProgressAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:20:00.000Z"},"finishedAt":{"format":"date-time","type":"string","nullable":true,"example":null},"errorCount":{"type":"number","example":0},"lastError":{"type":"string","nullable":true,"example":null}},"required":["case","source","status","chunking","embedding","summarization","overallPercent","errorCount"]},"AiAttorneyNotesDto":{"type":"object","properties":{"theory":{"type":"string","nullable":true},"strategy":{"type":"string","nullable":true},"openQuestions":{"type":"array","items":{"type":"string"}}}},"LegalClaimElementDto":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"}},"required":["id","description"]},"LegalClaimDto":{"type":"object","properties":{"id":{"type":"string"},"claim":{"type":"string"},"statuteCitation":{"type":"string","nullable":true},"elements":{"type":"array","items":{"$ref":"#/components/schemas/LegalClaimElementDto"}},"source":{"type":"string","enum":["complaint","attorney_stated","amended_filing"]}},"required":["id","claim"]},"KeyPartyDto":{"type":"object","properties":{"name":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"role":{"type":"string","enum":["petitioner","respondent","plaintiff","defendant","child","witness","third_party","other"]},"handles":{"type":"array","items":{"type":"string"}},"significance":{"type":"string","nullable":true}},"required":["name"]},"KeyDateDto":{"type":"object","properties":{"date":{"format":"date-time","type":"string"},"event":{"type":"string"},"significance":{"type":"string","nullable":true}},"required":["date","event"]},"RelevantDateRangeDto":{"type":"object","properties":{"start":{"format":"date-time","type":"string"},"end":{"format":"date-time","type":"string"}}},"AiCaseContextPatchDto":{"type":"object","properties":{"caseType":{"type":"string","nullable":true},"jurisdiction":{"type":"string","nullable":true},"theoryOfCase":{"type":"string","nullable":true},"whatToProve":{"type":"array","items":{"type":"string"}},"legalClaims":{"type":"array","items":{"$ref":"#/components/schemas/LegalClaimDto"}},"keyParties":{"type":"array","items":{"$ref":"#/components/schemas/KeyPartyDto"}},"keyDates":{"type":"array","items":{"$ref":"#/components/schemas/KeyDateDto"}},"relevantDateRange":{"$ref":"#/components/schemas/RelevantDateRangeDto"},"sourceDocuments":{"type":"array","items":{"type":"string"}}}},"StartFilingExtractionDto":{"type":"object","properties":{"caseFileId":{"type":"string","description":"Id of the CaseFile to extract from"}},"required":["caseFileId"]},"ApplyFilingExtractionDto":{"type":"object","properties":{"fieldsToApply":{"description":"Optional whitelist of fields to apply (e.g. [\"legalClaims\",\"keyParties\"]). Defaults to all extractable fields.","type":"array","items":{"type":"string"}}}},"RebuildEvidentiaryElementDto":{"type":"object","properties":{"claimId":{"type":"string"},"elementId":{"type":"string"}},"required":["claimId","elementId"]},"AttorneyOverrideDto":{"type":"object","properties":{"override":{"type":"string","nullable":true,"description":"Attorney-authored override; pass null to clear it."}}},"SuggestTagsDto":{"type":"object","properties":{"forceRefresh":{"type":"boolean","default":false,"description":"Bypass the cache and force a fresh Claude call."}}},"AiPromptUpdateDto":{"type":"object","properties":{"systemPrompt":{"type":"string"},"toolDescription":{"type":"string"},"toolInputSchema":{"type":"object","description":"Claude tool-use input_schema (JSON Schema). Renaming or removing top-level properties triggers 400."},"model":{"type":"string","description":"'haiku' / 'sonnet' alias or a literal Bedrock model id"},"maxTokens":{"type":"number","minimum":1,"maximum":32000},"temperature":{"type":"number","minimum":0,"maximum":2},"notes":{"type":"string"}}},"AiPromptCreateDraftDto":{"type":"object","properties":{"systemPrompt":{"type":"string"},"toolDescription":{"type":"string"},"toolInputSchema":{"type":"object","description":"Claude tool-use input_schema (JSON Schema). Renaming or removing top-level properties triggers 400."},"model":{"type":"string","description":"'haiku' / 'sonnet' alias or a literal Bedrock model id"},"maxTokens":{"type":"number","minimum":1,"maximum":32000},"temperature":{"type":"number","minimum":0,"maximum":2},"notes":{"type":"string"},"seedFromVersionSlug":{"type":"string","description":"Seed the new draft from this version slug. Defaults to the currently active version."}}},"SandboxPublicTokenResponseEntity":{"type":"object","properties":{"publicToken":{"type":"string","description":"Temporary public_token to use with the exchange endpoint."},"requestId":{"type":"string","description":"Request identifier for debugging and support."}},"required":["publicToken","requestId"]},"SandboxPublicTokenDto":{"type":"object","properties":{"institutionId":{"type":"string","description":"Plaid institution ID to simulate. Defaults to ins_109508 (First Platypus Bank).","default":"ins_109508"},"initialProducts":{"type":"array","default":["transactions","liabilities","investments","statements"],"description":"Plaid products to enable on the simulated Item. Defaults to [transactions, liabilities, investments, statements].","items":{"type":"string","enum":["assets","auth","beacon","employment","identity","income_verification","identity_verification","investments","liabilities","payment_initiation","standing_orders","signal","statements","transactions","transfer","cra_base_report","cra_income_insights","cra_cashflow_insights","cra_lend_score","cra_partner_insights","cra_network_insights","cra_monitoring","layer","protect_linked_bank"]}}}},"InvestmentTransactionDataEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"date":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"type":{"type":"string","description":"Transaction type (buy, sell, dividend, etc.)","example":"buy"},"subtype":{"type":"string","example":"buy"},"name":{"type":"string","description":"Transaction description / name.","example":"BUY Example Total Market ETF"},"tickerSymbol":{"type":"string","nullable":true,"example":"EXTM"},"securityName":{"type":"string","nullable":true,"example":"Example Total Market ETF"},"securityType":{"type":"string","nullable":true,"example":"etf"},"quantity":{"type":"number","nullable":true,"example":10},"price":{"type":"number","nullable":true,"description":"Price per unit of the security, in currency units (dollars for USD), as Plaid sends it.","example":250.5},"amount":{"type":"number","description":"In currency units (dollars for USD), as Plaid sends it. Positive when cash leaves the account (e.g. a buy), negative when cash comes in (e.g. a sell).","example":2505},"fees":{"type":"number","nullable":true,"description":"In currency units (dollars for USD), as Plaid sends it.","example":0},"isoCurrencyCode":{"type":"string","nullable":true,"example":"USD"},"financialAccountId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["id","date","type","subtype","name","amount","financialAccountId"]},"RemovePlaidItemDto":{"type":"object","properties":{"reasonCode":{"type":"string","enum":["FRAUD_FIRST_PARTY","FRAUD_FALSE_IDENTITY","FRAUD_ABUSE","FRAUD_OTHER","CONNECTION_IS_NON_FUNCTIONAL","OTHER"],"description":"Reason for removing the Plaid Item"},"reasonNote":{"type":"string","description":"Additional context about the reason. Must not contain PII.","maxLength":512}}},"AccountDataEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"plaidItemId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0a0"},"plaidAccountId":{"type":"string","example":"BxBXxLj1m4HMXBm9WZZmCWVbPjX16EHwv99vp"},"balanceAvailable":{"type":"number","nullable":true,"example":1250.4},"balanceCurrent":{"type":"number","nullable":true,"example":1310.75},"balanceLastUpdated":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T14:30:00.000Z"},"balanceLimit":{"type":"number","nullable":true,"example":null},"createdAt":{"format":"date-time","type":"string","example":"2026-01-10T09:00:00.000Z"},"holderCategory":{"type":"string","nullable":true,"example":"personal"},"currency":{"type":"string","nullable":true,"example":"USD"},"name":{"type":"string","example":"Everyday Checking"},"officialName":{"type":"string","nullable":true,"example":"Example Bank Everyday Checking"},"accountMask":{"type":"string","nullable":true,"description":"Last digits of the account number, as the bank shows them","example":"0042"},"subtype":{"type":"string","nullable":true,"example":"checking"},"type":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other","brokerage"]},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"financialAccountProviderId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"financialAccountProviderType":{"type":"string","enum":["plaid"]},"financialAccountProviderName":{"type":"string","nullable":true,"example":"Example Bank"},"extractionCodeId":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0b5"},"ocSharedStatus":{"type":"string","nullable":true,"enum":["shared","pending","not_gated"],"description":"Opposing-council only: whether this account has been shared with the OL. `null` for OL/OG callers — same convention as every other item listing. Transactions inherit their account's status, since FINANCIAL approvals are keyed on account ids."}},"required":["id","plaidItemId","plaidAccountId","createdAt","name","type","updatedAt","financialAccountProviderId","financialAccountProviderType"]},"TransactionTagData":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0a1"},"name":{"type":"string","example":"Responsive"}},"required":["id","name"]},"HoldingDataEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"financialAccount":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"assetName":{"type":"string","nullable":true,"example":"Example Index Fund"},"tickerSymbol":{"type":"string","nullable":true,"example":"EXIF"},"type":{"type":"string","nullable":true,"example":"mutual fund"},"subtype":{"type":"string","nullable":true,"example":null},"quantity":{"type":"number","nullable":true,"example":12.5},"price":{"type":"number","nullable":true,"example":101.2},"totalValue":{"type":"number","nullable":true,"example":1265},"costBasis":{"type":"number","nullable":true,"example":1180},"currency":{"type":"string","nullable":true,"example":"USD"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["id","financialAccount","assetName","tickerSymbol","type","subtype","quantity","price","totalValue","costBasis","currency"]},"AprEntity":{"type":"object","properties":{"aprPercentage":{"type":"number","example":24.99},"aprType":{"type":"string","example":"purchase_apr"},"balanceSubjectToApr":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":1562.32},"interestChargeAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":32.48}},"required":["aprPercentage","aprType"]},"CreditLiabilityEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"financialAccount":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"plaidAccountId":{"type":"string","example":"BxBXxLj1m4HMXBm9WZZmCWVbPjX16EHwv99vp"},"liabilityType":{"type":"string","example":"credit"},"aprs":{"type":"array","items":{"$ref":"#/components/schemas/AprEntity"}},"isOverdue":{"type":"boolean","nullable":true,"example":false},"lastPaymentAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":168.25},"lastPaymentDate":{"type":"string","nullable":true,"example":"2026-01-05"},"lastStatementIssueDate":{"type":"string","nullable":true,"example":"2026-01-10"},"lastStatementBalance":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":1708.77},"minimumPaymentAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":40.25},"nextPaymentDueDate":{"type":"string","nullable":true,"example":"2026-02-05"},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["id","financialAccount","plaidAccountId","liabilityType","aprs","createdAt","updatedAt"]},"InterestRateEntity":{"type":"object","properties":{"percentage":{"type":"number","nullable":true,"example":3.99},"type":{"type":"string","nullable":true,"example":"fixed"}}},"PropertyAddressEntity":{"type":"object","properties":{"city":{"type":"string","nullable":true,"example":"Springfield"},"country":{"type":"string","nullable":true,"example":"US"},"postalCode":{"type":"string","nullable":true,"example":"62701"},"region":{"type":"string","nullable":true,"example":"IL"},"street":{"type":"string","nullable":true,"example":"123 Main St"}}},"MortgageLiabilityEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"financialAccount":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"plaidAccountId":{"type":"string","example":"BxBXxLj1m4HMXBm9WZZmCWVbPjX16EHwv99vp"},"liabilityType":{"type":"string","example":"mortgage"},"accountNumber":{"type":"string","nullable":true,"description":"Last 4 characters of the loan account number","example":"4321"},"currentLateFee":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":25.5},"escrowBalance":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":3141.54},"hasPmi":{"type":"boolean","nullable":true,"example":true},"hasPrepaymentPenalty":{"type":"boolean","nullable":true,"example":false},"interestRate":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/InterestRateEntity"}]},"lastPaymentAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":1875.32},"lastPaymentDate":{"type":"string","nullable":true,"example":"2026-01-01"},"loanTypeDescription":{"type":"string","nullable":true,"example":"conventional"},"loanTerm":{"type":"string","nullable":true,"example":"30 year"},"maturityDate":{"type":"string","nullable":true,"example":"2051-02-01"},"nextMonthlyPayment":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":1875.32},"nextPaymentDueDate":{"type":"string","nullable":true,"example":"2026-02-01"},"originationDate":{"type":"string","nullable":true,"example":"2021-01-15"},"originationPrincipalAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":425000.5},"pastDueAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":1875.32},"propertyAddress":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/PropertyAddressEntity"}]},"ytdInterestPaid":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":1150.42},"ytdPrincipalPaid":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":724.9},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["id","financialAccount","plaidAccountId","liabilityType","createdAt","updatedAt"]},"LoanStatusEntity":{"type":"object","properties":{"endDate":{"type":"string","nullable":true,"example":"2033-08-20"},"type":{"type":"string","nullable":true,"example":"repayment"}}},"RepaymentPlanEntity":{"type":"object","properties":{"description":{"type":"string","nullable":true,"example":"Standard Repayment"},"type":{"type":"string","nullable":true,"example":"standard"}}},"ServicerAddressEntity":{"type":"object","properties":{"city":{"type":"string","nullable":true,"example":"Springfield"},"region":{"type":"string","nullable":true,"example":"IL"},"street":{"type":"string","nullable":true,"example":"456 Oak Ave"},"postalCode":{"type":"string","nullable":true,"example":"62704"},"country":{"type":"string","nullable":true,"example":"US"}}},"StudentLiabilityEntity":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"financialAccount":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e2"},"plaidAccountId":{"type":"string","example":"BxBXxLj1m4HMXBm9WZZmCWVbPjX16EHwv99vp"},"liabilityType":{"type":"string","example":"student"},"accountNumber":{"type":"string","nullable":true,"description":"Last 4 characters of the loan account number","example":"4321"},"disbursementDates":{"example":["2018-08-20"],"type":"array","items":{"type":"string"}},"expectedPayoffDate":{"type":"string","nullable":true,"example":"2033-08-20"},"guarantor":{"type":"string","nullable":true,"example":"DEPT OF ED"},"interestRatePercentage":{"type":"number","example":5.25},"isOverdue":{"type":"boolean","nullable":true,"example":false},"lastPaymentAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":138.05},"lastPaymentDate":{"type":"string","nullable":true,"example":"2026-01-12"},"lastStatementBalance":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":18250.75},"lastStatementIssueDate":{"type":"string","nullable":true,"example":"2025-12-28"},"loanName":{"type":"string","nullable":true,"example":"Consolidation"},"loanStatus":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/LoanStatusEntity"}]},"minimumPaymentAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":138.05},"nextPaymentDueDate":{"type":"string","nullable":true,"example":"2026-02-12"},"originationDate":{"type":"string","nullable":true,"example":"2018-08-20"},"originationPrincipalAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":25000.5},"outstandingInterestAmount":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":612.36},"paymentReferenceNumber":{"type":"string","nullable":true,"example":"4307799999"},"repaymentPlan":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/RepaymentPlanEntity"}]},"sequenceNumber":{"type":"string","nullable":true,"example":"1"},"servicerAddress":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/ServicerAddressEntity"}]},"ytdInterestPaid":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":79.84},"ytdPrincipalPaid":{"type":"number","nullable":true,"description":"In currency units (e.g. dollars), not cents, as reported by Plaid.","example":58.21},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["id","financialAccount","plaidAccountId","liabilityType","disbursementDates","interestRatePercentage","createdAt","updatedAt"]},"GetLiabilitiesResponseEntity":{"type":"object","properties":{"total":{"type":"number","example":42},"page":{"type":"number","example":1},"perPage":{"type":"number","example":20},"skip":{"type":"number","example":0},"limit":{"type":"number","example":20},"totalPages":{"type":"number","example":3},"nextPage":{"type":"number","example":2,"nullable":true},"previousPage":{"type":"number","example":null,"nullable":true},"creditLiabilities":{"type":"array","items":{"$ref":"#/components/schemas/CreditLiabilityEntity"}},"mortgageLiabilities":{"type":"array","items":{"$ref":"#/components/schemas/MortgageLiabilityEntity"}},"studentLiabilities":{"type":"array","items":{"$ref":"#/components/schemas/StudentLiabilityEntity"}}},"required":["total","totalPages","nextPage","previousPage","creditLiabilities","mortgageLiabilities","studentLiabilities"]},"RetrySyncDto":{"type":"object","properties":{"products":{"type":"array","description":"Products to re-trigger a sync for. Omit to retry every retryable product on the item.","items":{"type":"string","enum":["transactions","recurringTransactions","investments","liabilities"]}}}},"TransactionDataEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0c1"},"plaidItemId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0a0"},"plaidTransactionId":{"type":"string","example":"lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqDje"},"amount":{"type":"number","description":"Absolute value of the transaction amount.","example":42.18},"transactionType":{"type":"string","enum":["debit","credit"],"description":"debit = money out, credit = money in."},"authorizedDate":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-13T00:00:00.000Z"},"detailed":{"type":"string","nullable":true,"example":"FOOD_AND_DRINK_COFFEE"},"category":{"type":"string","nullable":true,"description":"Effective category: the user override when set, otherwise Plaid’s primary category.","example":"FOOD_AND_DRINK"},"categoryOverridden":{"type":"boolean","description":"True when `category` comes from a user override rather than Plaid.","example":false},"createdAt":{"format":"date-time","type":"string","example":"2026-01-14T08:00:00.000Z"},"transactionDate":{"format":"date-time","type":"string","example":"2026-01-14T00:00:00.000Z"},"financialAccount":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"currency":{"type":"string","nullable":true,"example":"USD"},"merchantName":{"type":"string","nullable":true,"example":"Corner Coffee"},"name":{"type":"string","example":"CORNER COFFEE 1123"},"paymentChannel":{"type":"string","example":"in store"},"pending":{"type":"boolean","example":false},"pendingTransactionId":{"type":"string","nullable":true,"example":null},"plaidAccountId":{"type":"string","example":"BxBXxLj1m4HMXBm9WZZmCWVbPjX16EHwv99vp"},"transactionCode":{"type":"string","nullable":true,"example":null},"unofficialCurrencyCode":{"type":"string","nullable":true,"example":null},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-14T08:00:00.000Z"},"isRecurring":{"type":"boolean","description":"True if this transaction belongs to a recurring stream.","example":false},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["id","plaidItemId","plaidTransactionId","amount","transactionType","categoryOverridden","createdAt","transactionDate","financialAccount","name","paymentChannel","pending","plaidAccountId","updatedAt","isRecurring"]},"SubcategorySummaryEntry":{"type":"object","properties":{"subcategory":{"type":"string","description":"Normalized subcategory name (uppercase). Transactions with no Plaid subcategory or whose primary category was overridden fall under a bucket labelled 'UNCATEGORIZED' in the per-account summary (null in the monthly summary).","nullable":true,"example":"FOOD_AND_DRINK_COFFEE"},"debitTotal":{"type":"number","description":"Sum of positive amounts (money out) for this subcategory. In the account currency's major unit (dollars for USD), not cents, rounded to 2 decimals.","example":64.2},"creditTotal":{"type":"number","description":"Sum of absolute negative amounts (money in) for this subcategory. In the account currency's major unit (dollars for USD), not cents, rounded to 2 decimals.","example":0},"transactionCount":{"type":"number","description":"Total number of transactions in this subcategory.","example":9}},"required":["subcategory","debitTotal","creditTotal","transactionCount"]},"CategoryBreakdownEntry":{"type":"object","properties":{"category":{"type":"string","description":"Normalized category name (uppercase). Null for uncategorized transactions.","nullable":true,"example":"FOOD_AND_DRINK"},"debitTotal":{"type":"number","description":"Sum of positive amounts (money out) for this category. In the account currency's major unit (dollars for USD), not cents, rounded to 2 decimals.","example":482.35},"creditTotal":{"type":"number","description":"Sum of absolute negative amounts (money in) for this category. In the account currency's major unit (dollars for USD), not cents, rounded to 2 decimals.","example":0},"transactionCount":{"type":"number","description":"Total number of transactions in this category.","example":17},"subcategories":{"description":"Per-subcategory breakdown, present only when groupBy=detailed. Sorted by debitTotal desc, uncategorized (null) last.","type":"array","items":{"$ref":"#/components/schemas/SubcategorySummaryEntry"}}},"required":["category","debitTotal","creditTotal","transactionCount"]},"TransactionSummaryTotals":{"type":"object","properties":{"debitTotal":{"type":"number","description":"Total sum of positive amounts (money out) across all matched transactions. In the account currency's major unit (dollars for USD), not cents, rounded to 2 decimals.","example":3120.75},"creditTotal":{"type":"number","description":"Total sum of absolute negative amounts (money in) across all matched transactions. In the account currency's major unit (dollars for USD), not cents, rounded to 2 decimals.","example":4500},"transactionCount":{"type":"number","description":"Total number of matched transactions.","example":86}},"required":["debitTotal","creditTotal","transactionCount"]},"GetTransactionsSummaryResponseEntity":{"type":"object","properties":{"categories":{"type":"array","items":{"$ref":"#/components/schemas/CategoryBreakdownEntry"}},"totals":{"$ref":"#/components/schemas/TransactionSummaryTotals"}},"required":["categories","totals"]},"MonthlyCategoryBreakdownEntry":{"type":"object","properties":{"category":{"type":"string","description":"Normalized category name (uppercase). Null for uncategorized transactions.","nullable":true,"example":"FOOD_AND_DRINK"},"debitTotal":{"type":"number","description":"Sum of positive amounts (money out) for this category. In the account currency's major unit (dollars for USD), not cents, rounded to 2 decimals.","example":482.35},"creditTotal":{"type":"number","description":"Sum of absolute negative amounts (money in) for this category. In the account currency's major unit (dollars for USD), not cents, rounded to 2 decimals.","example":0},"transactionCount":{"type":"number","description":"Total number of transactions in this category.","example":17},"subcategories":{"description":"Per-subcategory breakdown, present only when groupBy=detailed. Sorted by debitTotal desc, uncategorized (null) last.","type":"array","items":{"$ref":"#/components/schemas/SubcategorySummaryEntry"}}},"required":["category","debitTotal","creditTotal","transactionCount"]},"MonthlyCategorySummaryEntry":{"type":"object","properties":{"month":{"type":"string","description":"Month bucket in UTC. Format: YYYY-MM.","example":"2024-01"},"categories":{"description":"Per-category debit/credit totals for this month, sorted by debitTotal desc.","type":"array","items":{"$ref":"#/components/schemas/MonthlyCategoryBreakdownEntry"}},"totals":{"description":"Totals across all categories for this month.","allOf":[{"$ref":"#/components/schemas/TransactionSummaryTotals"}]}},"required":["month","categories","totals"]},"GetMonthlyCategorySummaryResponseEntity":{"type":"object","properties":{"months":{"description":"One entry per month that has matching transactions, ascending.","type":"array","items":{"$ref":"#/components/schemas/MonthlyCategorySummaryEntry"}},"totals":{"description":"Grand totals across every month and category.","allOf":[{"$ref":"#/components/schemas/TransactionSummaryTotals"}]},"accountIds":{"description":"FinancialAccount IDs included in the aggregation.","example":["507f1f77bcf86cd799439011","65a1b2c3d4e5f6a7b8c9d0e2"],"type":"array","items":{"type":"string"}}},"required":["months","totals","accountIds"]},"CaseSubcategoryEntry":{"type":"object","properties":{"subcategory":{"type":"string","description":"Normalized subcategory name (uppercase). Null for transactions with no Plaid subcategory or whose primary category was overridden.","nullable":true,"example":"FOOD_AND_DRINK_RESTAURANT"},"transactionCount":{"type":"number","description":"Number of transactions in this subcategory across the included accounts.","example":12}},"required":["subcategory","transactionCount"]},"CaseCategoryEntry":{"type":"object","properties":{"category":{"type":"string","description":"Normalized category name (uppercase). Null for uncategorized transactions.","nullable":true,"example":"FOOD_AND_DRINK"},"transactionCount":{"type":"number","description":"Number of transactions in this category across the included accounts.","example":37},"subcategories":{"description":"Subcategories nested under this category, sorted alphabetically with uncategorized (null) last. Empty when the category has no subcategories.","type":"array","items":{"$ref":"#/components/schemas/CaseSubcategoryEntry"}}},"required":["category","transactionCount","subcategories"]},"GetCaseCategoriesResponseEntity":{"type":"object","properties":{"categories":{"description":"Distinct categories present across the included accounts, sorted alphabetically with uncategorized last.","type":"array","items":{"$ref":"#/components/schemas/CaseCategoryEntry"}},"accountIds":{"description":"FinancialAccount IDs included in the lookup.","example":["507f1f77bcf86cd799439011"],"type":"array","items":{"type":"string"}}},"required":["categories","accountIds"]},"UpdateTransactionCategoryResponseEntity":{"type":"object","properties":{"id":{"type":"string","description":"Transaction ID.","example":"507f1f77bcf86cd799439011"},"category":{"type":"string","description":"Effective category after the update (override if set, otherwise Plaid’s value). Null when uncategorized.","nullable":true,"example":"MEDICAL"},"categoryOverridden":{"type":"boolean","description":"True when the effective category comes from a user override rather than Plaid.","example":true},"updatedCount":{"type":"number","description":"Total number of transactions updated by this request, including the target. Greater than 1 only when applyToSameMerchant matched additional transactions.","example":1}},"required":["id","category","categoryOverridden","updatedCount"]},"UpdateTransactionCategoryDto":{"type":"object","properties":{"category":{"type":"string","description":"New category for the transaction. An existing category to reassign, or a new label to create. Stored normalized (trimmed, uppercased). Omit or send null to reset to the original Plaid category.","nullable":true,"example":"MEDICAL"},"applyToSameMerchant":{"type":"boolean","description":"When true, apply the same category to every other transaction from the same merchant (exact merchant-name match) across the case. No-op when the transaction has no merchant name. One-time update of existing transactions only.","default":false},"accountIds":{"description":"Restrict the bulk apply to these FinancialAccount IDs (all must belong to the case). Omit to apply across every linked account in the case. Only used when applyToSameMerchant is true.","type":"array","items":{"type":"string"}}}},"RecurringTransactionStreamDataEntity":{"type":"object","properties":{"id":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0d1"},"plaidItemId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0a0"},"streamId":{"type":"string","example":"no86Eox18VHMvaOVL7gPUM9ap3aR1LsAVZ5nc"},"averageAmount":{"type":"number","nullable":true,"example":15.49},"averageAmountCurrency":{"type":"string","nullable":true,"example":"USD"},"detailed":{"type":"string","nullable":true,"example":"ENTERTAINMENT_TV_AND_MOVIES"},"category":{"type":"string","nullable":true,"example":"ENTERTAINMENT"},"createdAt":{"format":"date-time","type":"string","example":"2026-01-10T09:00:00.000Z"},"description":{"type":"string","example":"STREAMING SERVICE"},"financialAccount":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0e1"},"firstDate":{"format":"date-time","type":"string","example":"2025-03-01T00:00:00.000Z"},"frequency":{"type":"string","example":"MONTHLY"},"isActive":{"type":"boolean","example":true},"lastAmount":{"type":"number","nullable":true,"example":15.49},"lastDate":{"format":"date-time","type":"string","example":"2026-01-01T00:00:00.000Z"},"merchantName":{"type":"string","nullable":true,"example":"Streaming Service"},"plaidAccountId":{"type":"string","example":"BxBXxLj1m4HMXBm9WZZmCWVbPjX16EHwv99vp"},"predictedNextDate":{"format":"date-time","type":"string","nullable":true,"example":"2026-02-01T00:00:00.000Z"},"status":{"type":"string","example":"MATURE"},"transactionIds":{"example":["lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqDje"],"type":"array","items":{"type":"string"}},"type":{"type":"string","enum":["inflow","outflow"],"example":"outflow"},"updatedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TransactionTagData"}},"attachedTags":{"type":"array","items":{"$ref":"#/components/schemas/AttachedTagResponseEntity"}}},"required":["id","plaidItemId","streamId","createdAt","description","financialAccount","firstDate","frequency","isActive","lastDate","plaidAccountId","status","transactionIds","type","updatedAt"]},"AddTransactionTagsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID this transaction belongs to."},"tagNames":{"minItems":1,"maxItems":100,"description":"Array of tag names or tag IDs to add.","type":"array","items":{"type":"string"}}},"required":["caseId","tagNames"]},"RemoveTransactionTagsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID this transaction belongs to."},"tagNames":{"minItems":1,"maxItems":100,"description":"Array of tag names or tag IDs to remove.","type":"array","items":{"type":"string"}}},"required":["caseId","tagNames"]},"BulkAddTransactionTagsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID these transactions belong to."},"transactionIds":{"minItems":1,"maxItems":500,"description":"Array of transaction IDs to tag.","type":"array","items":{"type":"string"}},"tagNames":{"minItems":1,"maxItems":100,"description":"Array of tag names or tag IDs to add.","type":"array","items":{"type":"string"}}},"required":["caseId","transactionIds","tagNames"]},"BulkAddRecurringTransactionTagsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID these recurring transaction streams belong to."},"streamIds":{"minItems":1,"maxItems":500,"description":"Array of recurring transaction stream IDs to tag.","type":"array","items":{"type":"string"}},"tagNames":{"minItems":1,"maxItems":100,"description":"Array of tag names or tag IDs to add.","type":"array","items":{"type":"string"}}},"required":["caseId","streamIds","tagNames"]},"BulkAddLiabilityTagsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID these liabilities belong to."},"liabilityIds":{"minItems":1,"maxItems":500,"description":"Array of liability IDs to tag.","type":"array","items":{"type":"string"}},"tagNames":{"minItems":1,"maxItems":100,"description":"Array of tag names or tag IDs to add.","type":"array","items":{"type":"string"}}},"required":["caseId","liabilityIds","tagNames"]},"BulkAddHoldingTagsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID these holdings belong to."},"holdingIds":{"minItems":1,"maxItems":500,"description":"Array of holding IDs to tag.","type":"array","items":{"type":"string"}},"tagNames":{"minItems":1,"maxItems":100,"description":"Array of tag names or tag IDs to add.","type":"array","items":{"type":"string"}}},"required":["caseId","holdingIds","tagNames"]},"BulkAddInvestmentTransactionTagsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID these investment transactions belong to."},"investmentTransactionIds":{"minItems":1,"maxItems":500,"description":"Array of investment transaction IDs to tag.","type":"array","items":{"type":"string"}},"tagNames":{"minItems":1,"maxItems":100,"description":"Array of tag names or tag IDs to add.","type":"array","items":{"type":"string"}}},"required":["caseId","investmentTransactionIds","tagNames"]},"ExportTransactionFiltersDto":{"type":"object","properties":{"startDate":{"type":"string","description":"Optional start date filter (YYYY-MM-DD, inclusive).","example":"2024-01-01"},"endDate":{"type":"string","description":"Optional end date filter (YYYY-MM-DD, inclusive).","example":"2024-12-31"},"category":{"type":"string","description":"Filter by transaction category (categoryPrimary). Case-insensitive partial match."},"transactionType":{"type":"string","description":"Filter by transaction type. debit = money out (positive in Plaid), credit = money in (negative in Plaid).","enum":["debit","credit"]},"minAmount":{"type":"number","description":"Filter transactions with amount >= minAmount."},"maxAmount":{"type":"number","description":"Filter transactions with amount <= maxAmount (absolute value)."},"tagIds":{"description":"Filter by tag IDs. Only exports transactions that have at least one of the provided tags.","type":"array","items":{"type":"string"}}}},"ExportTransactionsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID this financial data belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters to narrow down exported records.","allOf":[{"$ref":"#/components/schemas/ExportTransactionFiltersDto"}]}},"required":["caseId","format"]},"ExportPlaidItemFiltersDto":{"type":"object","properties":{"startDate":{"type":"string","description":"Optional start date filter (YYYY-MM-DD, inclusive).","example":"2024-01-01"},"endDate":{"type":"string","description":"Optional end date filter (YYYY-MM-DD, inclusive).","example":"2024-12-31"},"includeAccountTypes":{"type":"array","description":"Only include accounts of these types. If omitted, all types are included.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}},"excludeAccountTypes":{"type":"array","description":"Exclude accounts of these types.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}},"category":{"type":"string","description":"Transaction category (partial, case-insensitive)."},"transactionType":{"type":"string","enum":["debit","credit"]},"minAmount":{"type":"number"},"maxAmount":{"type":"number"},"tagIds":{"type":"array","items":{"type":"array"}}}},"ExportPlaidItemTransactionsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID that the PlaidItem belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters to narrow down exported records.","allOf":[{"$ref":"#/components/schemas/ExportPlaidItemFiltersDto"}]}},"required":["caseId","format"]},"ExportRecurringTransactionFiltersDto":{"type":"object","properties":{"type":{"type":"string","description":"Filter by stream type (inflow / outflow).","enum":["inflow","outflow"]},"isActive":{"type":"boolean","description":"Filter by active status."},"frequency":{"type":"string","description":"Filter by frequency (e.g. WEEKLY, MONTHLY)."},"tagIds":{"description":"Filter by tag IDs. Only exports streams that have at least one of the provided tags.","type":"array","items":{"type":"string"}}}},"ExportRecurringTransactionsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID this financial data belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters to narrow down exported records.","allOf":[{"$ref":"#/components/schemas/ExportRecurringTransactionFiltersDto"}]}},"required":["caseId","format"]},"ExportPlaidItemRecurringFiltersDto":{"type":"object","properties":{"includeAccountTypes":{"type":"array","description":"Only include accounts of these types. If omitted, all types are included.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}},"excludeAccountTypes":{"type":"array","description":"Exclude accounts of these types.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}}}},"ExportPlaidItemRecurringTransactionsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID that the PlaidItem belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters to narrow down exported accounts.","allOf":[{"$ref":"#/components/schemas/ExportPlaidItemRecurringFiltersDto"}]}},"required":["caseId","format"]},"ExportHoldingFiltersDto":{"type":"object","properties":{"type":{"type":"string","description":"Filter by security type (e.g. equity, etf, derivative, mutual fund)."},"tagIds":{"description":"Filter by tag IDs. Only exports holdings that have at least one of the provided tags.","type":"array","items":{"type":"string"}}}},"ExportHoldingsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID this financial data belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters to narrow down exported holdings.","allOf":[{"$ref":"#/components/schemas/ExportHoldingFiltersDto"}]}},"required":["caseId","format"]},"ExportPlaidItemHoldingFiltersDto":{"type":"object","properties":{"includeAccountTypes":{"type":"array","description":"Only include accounts of these types. If omitted, all types are included.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}},"excludeAccountTypes":{"type":"array","description":"Exclude accounts of these types.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}}}},"ExportPlaidItemHoldingsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID that the PlaidItem belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters to narrow down exported accounts.","allOf":[{"$ref":"#/components/schemas/ExportPlaidItemHoldingFiltersDto"}]}},"required":["caseId","format"]},"ExportLiabilityFiltersDto":{"type":"object","properties":{"liabilityType":{"type":"string","description":"Filter by liability type.","enum":["credit","mortgage","student"]},"isOverdue":{"type":"boolean","description":"Filter by overdue status. Applies to credit and student liabilities only."},"tagIds":{"description":"Filter by tag IDs. Only exports liabilities that have at least one of the provided tags.","type":"array","items":{"type":"string"}}}},"ExportLiabilitiesDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID this financial data belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters to narrow down exported liabilities.","allOf":[{"$ref":"#/components/schemas/ExportLiabilityFiltersDto"}]}},"required":["caseId","format"]},"ExportPlaidItemLiabilityFiltersDto":{"type":"object","properties":{"includeAccountTypes":{"type":"array","description":"Only include accounts of these types. If omitted, all types are included.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}},"excludeAccountTypes":{"type":"array","description":"Exclude accounts of these types.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}}}},"ExportPlaidItemLiabilitiesDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID that the PlaidItem belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters to narrow down exported accounts.","allOf":[{"$ref":"#/components/schemas/ExportPlaidItemLiabilityFiltersDto"}]}},"required":["caseId","format"]},"ExportAccountInvestmentTransactionFiltersDto":{"type":"object","properties":{"startDate":{"type":"string","description":"Start date filter (YYYY-MM-DD, inclusive).","example":"2024-01-01"},"endDate":{"type":"string","description":"End date filter (YYYY-MM-DD, inclusive).","example":"2024-12-31"},"transactionType":{"type":"string","description":"Filter by transaction type (e.g. buy, sell, dividend, transfer).","example":"sell"},"tagIds":{"description":"Filter by tag IDs (reserved for future use).","type":"array","items":{"type":"string"}}}},"ExportAccountInvestmentTransactionsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID this financial data belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters.","allOf":[{"$ref":"#/components/schemas/ExportAccountInvestmentTransactionFiltersDto"}]}},"required":["caseId","format"]},"ExportInvestmentTransactionFiltersDto":{"type":"object","properties":{"includeAccountTypes":{"type":"array","description":"Only include accounts of these types. If omitted, all types are included.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}},"excludeAccountTypes":{"type":"array","description":"Exclude accounts of these types.","default":[],"items":{"type":"string","enum":["depository","credit","loan","investment","brokerage","other"]}}}},"ExportInvestmentTransactionsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID that the PlaidItem belongs to."},"format":{"type":"string","description":"Export format.","enum":["csv","pdf"]},"filters":{"description":"Optional filters to narrow down exported accounts.","allOf":[{"$ref":"#/components/schemas/ExportInvestmentTransactionFiltersDto"}]}},"required":["caseId","format"]},"ListFinancialExportJobsItemEntity":{"type":"object","properties":{"jobId":{"type":"string","description":"Export job ID","example":"65a1b2c3d4e5f6a7b8c9d0e9"},"exportType":{"type":"string","enum":["transactions","recurring-transactions","holdings","liabilities","investment-transactions","statements-bundle"]},"format":{"type":"string","enum":["csv","pdf","zip"]},"status":{"type":"string","enum":["pending","in-progress","completed","failed","cancelled"]},"fileName":{"type":"string","example":"transactions-2026-01-15.csv"},"downloadUrl":{"type":"string","example":"https://files.example.com/exports/transactions-2026-01-15.csv?signature=abc123"},"errorMessage":{"type":"string","example":null,"nullable":true},"createdAt":{"format":"date-time","type":"string","example":"2026-01-15T14:30:00.000Z"},"completedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:31:10.000Z"}},"required":["jobId","exportType","format","status","createdAt"]},"FinancialExportJobResponseEntity":{"type":"object","properties":{"jobId":{"type":"string","description":"Export job ID","example":"65a1b2c3d4e5f6a7b8c9d0e9"},"status":{"type":"string","enum":["pending","in-progress","completed","failed","cancelled"]},"downloadUrl":{"type":"string","description":"Pre-signed S3 download URL","example":"https://files.example.com/exports/transactions-2026-01-15.csv?signature=abc123"},"fileName":{"type":"string","description":"Generated file name","example":"transactions-2026-01-15.csv","nullable":true},"completedAt":{"format":"date-time","type":"string","example":"2026-01-15T14:31:10.000Z","nullable":true},"errorMessage":{"type":"string","example":null,"nullable":true}},"required":["jobId","status"]},"BulkDownloadStatementsResponseEntity":{"type":"object","properties":{"jobId":{"type":"string","description":"Export job ID. Poll GET /financial-data/export/:jobId for status and the signed download URL when complete.","example":"507f1f77bcf86cd799439011"}},"required":["jobId"]},"BulkDownloadStatementsDto":{"type":"object","properties":{"caseId":{"type":"string","description":"Case ID that the PlaidItem belongs to."}},"required":["caseId"]},"PartnerContactDto":{"type":"object","properties":{"givenName":{"type":"string"},"familyName":{"type":"string"},"email":{"type":"string"}},"required":["givenName","familyName","email"]},"PartnerAddressDto":{"type":"object","properties":{"street":{"type":"string"},"city":{"type":"string"},"region":{"type":"string"},"postalCode":{"type":"string"},"countryCode":{"type":"string","description":"ISO-3166-1 alpha-2 country code. Defaults to \"US\" on the backend if omitted.","example":"US"}},"required":["street","city","region","postalCode"]},"CreatePartnerCustomerDto":{"type":"object","properties":{"companyName":{"type":"string","description":"Legal company name"},"legalEntityName":{"type":"string","description":"Legal entity name (may differ from company name)"},"website":{"type":"string","description":"Company website"},"applicationName":{"type":"string","description":"Name of the application using Plaid. Defaults to companyName if omitted."},"logo":{"type":"string","description":"Company logo (base64-encoded PNG)"},"technicalContact":{"$ref":"#/components/schemas/PartnerContactDto"},"billingContact":{"$ref":"#/components/schemas/PartnerContactDto"},"address":{"$ref":"#/components/schemas/PartnerAddressDto"},"isDiligenceAttested":{"type":"boolean","description":"Attests the information provided is accurate and the end customer agrees to Plaid's terms. Must be true."}},"required":["companyName","legalEntityName","website","isDiligenceAttested"]},"LiveExportQueueStateEntity":{"type":"object","properties":{"bullJobId":{"type":"string","example":"1842"},"state":{"type":"string","enum":["waiting","active","delayed","paused"],"example":"active"},"attemptsMade":{"type":"number","example":0}},"required":["bullJobId","state","attemptsMade"]},"LiveExportRowEntity":{"type":"object","properties":{"exportId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"caseId":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e2"},"caseName":{"type":"string","nullable":true,"example":"Doe v. Roe"},"organizationId":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e3"},"requestedBy":{"type":"string","nullable":true,"example":"65a1b2c3d4e5f6a7b8c9d0e4"},"status":{"type":"string","example":"inprogress"},"type":{"type":"string","example":"pdf"},"source":{"type":"string","example":"conversation"},"exportStartedAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:15:00.000Z"},"lastProgressAt":{"format":"date-time","type":"string","nullable":true,"example":"2026-01-15T10:20:00.000Z"},"heartbeatAgeMs":{"type":"number","nullable":true,"description":"Milliseconds since lastProgressAt, or null if never set.","example":45000},"conversationsProcessed":{"type":"number","example":12},"totalConversations":{"type":"number","example":40},"messagesProcessed":{"type":"number","example":3100},"totalMessages":{"type":"number","example":9800},"progressUnreliable":{"type":"boolean","description":"True when a stored progress counter exceeds its own total — a known corruption in rows pre-dating the Phase 1 counter reset. The raw counters are still reported as-is; this only flags them.","example":false},"errorMessage":{"type":"string","nullable":true,"example":null},"jobId":{"type":"string","nullable":true,"description":"The chain id shared by every chunk of a multi-job export (uuidV4). NOT a Bull job id — see queue.bullJobId for that. null on an export that was never chunked.","example":null},"jobIndex":{"type":"number","nullable":true,"description":"0-based position of this row within its jobId chain.","example":null},"totalJobs":{"type":"number","description":"Total chunks in this row's jobId chain; 1 when unchunked.","example":1},"queue":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/LiveExportQueueStateEntity"}]},"dequeuedAt":{"format":"date-time","type":"string","nullable":true,"description":"When an admin's queue operation MAY have left this export with no queue job — DELETE /admin/exports/:id/job, or a POST /admin/exports/:id/priority that did not end with the job back in the queue. Written before the queue is touched, so an operation interrupted midway still leaves a row this view can explain, and dropped again once the job provably survived. A record of what was done, not of what is true now — read awaitingRequeue for that.","example":null},"awaitingRequeue":{"type":"boolean","description":"True when an admin's queue operation removed this export's queue job and nothing has put one back: it will not run until it is re-run or resumed. Requires ALL THREE of the dequeue stamp, no live queue job, and a not-started status — so an export caught mid-reprioritise (a remove + re-add, briefly jobless) is not reported as waiting for an operator, nor is one that has since been cancelled or picked up, while one whose re-add failed outright is.","example":false}},"required":["exportId","caseId","caseName","organizationId","requestedBy","status","type","source","exportStartedAt","lastProgressAt","heartbeatAgeMs","conversationsProcessed","totalConversations","messagesProcessed","totalMessages","progressUnreliable","errorMessage","jobId","jobIndex","totalJobs","queue","dequeuedAt","awaitingRequeue"]},"LiveExportListResponseEntity":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/LiveExportRowEntity"}},"total":{"type":"number","description":"Total rows matching the filter, pre-page.","example":1}},"required":["rows","total"]},"CancelExportResponseEntity":{"type":"object","properties":{"exportId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"cancelRequested":{"type":"boolean","description":"Always true on a successful response: the cancel request is recorded (or was already recorded by an earlier call).","example":true},"jobRemoved":{"type":"boolean","description":"True only when a live queued job was found for this export and removed outright. Read \"effectiveAt\", not this, to tell whether the cancellation took effect immediately: a not-yet-started export whose job was already absent is cancelled immediately with nothing to remove, and reports false here.","example":false},"effectiveAt":{"type":"string","enum":["immediately","next-conversation-boundary","already-cancelled"],"description":"When the cancellation actually takes effect. \"immediately\" when the export had not started yet, so it is marked cancelled on the spot and will never run. A RUNNING export always stops at its next conversation boundary — cancellation is cooperative, never an instant stop.","example":"next-conversation-boundary"},"message":{"type":"string","description":"Plain-English summary of what happened, safe to show as-is.","example":"Cancellation requested; the export stops at its next conversation boundary."}},"required":["exportId","cancelRequested","jobRemoved","effectiveAt","message"]},"ResumeExportResponseEntity":{"type":"object","properties":{"exportId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"resumed":{"type":"boolean","description":"Always true on a successful response: the export has been re-queued and will restore what a previous attempt finished.","example":true},"resumedFrom":{"type":"string","enum":["not-started","inprogress","completed","failed","cancelled"],"description":"The status the export was resumed from. Only failed, cancelled and inprogress (the stuck case) exports can be resumed.","example":"failed"},"attemptCount":{"type":"number","description":"How many times this export has been re-started. Counts resumes only — a first run and a re-run do not bump it.","example":1},"restorableConversations":{"type":"number","description":"Conversations a previous attempt already finished and checkpointed — an upper bound on what this run restores, not a promise: a checkpoint is only reused when it was rendered with the same render settings this run picks up, and anything else is re-rendered. Zero is a legitimate resume: an export that died on its first conversation simply starts over.","example":12},"message":{"type":"string","description":"Plain-English summary of what happened, safe to show as-is.","example":"Export re-queued; it restores up to 12 finished conversations."}},"required":["exportId","resumed","resumedFrom","attemptCount","restorableConversations","message"]},"ReprioritizeExportResponseEntity":{"type":"object","properties":{"exportId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"position":{"type":"string","enum":["top","bottom"],"description":"Which end of the wait list the job was moved to.","example":"top"},"reordered":{"type":"boolean","description":"Always true on a successful (non-throwing) response.","example":true},"bullJobId":{"type":"string","description":"The job's NEW queue id. Reordering is physically a remove and re-add, so the id you saw a moment ago no longer exists — it is reported here rather than silently swapped.","example":"1907"},"previousBullJobId":{"type":"string","description":"The id the job had before the move, so a stale view can be matched up against the new one.","example":"1842"},"message":{"type":"string","description":"Plain-English summary of what happened, safe to show as-is.","example":"Export moved to the top of the queue with a new queue job id."}},"required":["exportId","position","reordered","bullJobId","previousBullJobId","message"]},"ReprioritizeExportDto":{"type":"object","properties":{"position":{"type":"string","enum":["top","bottom"],"description":"Which end of the export wait list to move this export to. \"top\" makes it the next export a worker picks up; \"bottom\" puts it behind everything currently waiting."}},"required":["position"]},"DequeueExportResponseEntity":{"type":"object","properties":{"exportId":{"type":"string","example":"65a1b2c3d4e5f6a7b8c9d0f1"},"jobRemoved":{"type":"boolean","description":"False when nothing was removed — no live queue job was FOUND for the export, or the job was gone by the time the removal ran. A legitimate outcome rather than a failure, because the end state you asked for appears to already hold; read the message, which is careful to say \"appears\", because nothing here can prove a negative about the queue.","example":true},"removedFromState":{"type":"string","nullable":true,"description":"The queue state the removed job was in; null when nothing was removed.","example":"waiting"},"status":{"type":"string","enum":["not-started","inprogress","completed","failed","cancelled"],"description":"What the export row reads now. Always notstarted — a dequeue is queue hygiene, not a cancellation, and leaves the export re-queueable.","example":"not-started"},"message":{"type":"string","description":"Plain-English summary of what happened, safe to show as-is.","example":"Queue job removed; the export is not started and stays re-queueable."}},"required":["exportId","jobRemoved","removedFromState","status","message"]}}}}