""" The read surface of the Audio Audit API, composed from the accounts, audio, billing and blog schemas. Much of it answers without credentials — pricing, the check, artifact and enhancement catalogues, report templates, blog posts, feature flags, credit estimates, podcast search, and a report or podcast fetched by id — while the fields that list or reach a workspace's own data need a session cookie, an API key sent as `Authorization: Bearer `, or a JWT sent as `Authorization: JWT `. Those fields answer for the caller's personal workspace unless an `organisationId` argument names one of their organisations. """ type Query { post(id: UUID, slug: String): PostType allPosts(category: String, ordering: String): [PostType] allPlans: [PlanType] plan(slug: String): PlanType creditPacks: [CreditPackType] checkTypes: [CheckTypeType] artifactTypes(userSelectable: Boolean): [ArtifactTypeType] enhancementTypes: [EnhancementTypeType] reportTemplates(organisationId: UUID): [ReportTemplateType] creditBalance(organisationId: UUID): String creditHistory(limit: Int, organisationId: UUID): [CreditTransactionType] estimateReportCredits(durationMs: Float, checkIds: [UUID], artifactIds: [UUID], enhancementIds: [UUID], reportTemplateId: UUID): String report(id: UUID): ReportType reports(limit: Int, offset: Int, organisationId: UUID, podcastId: UUID, forPodcastId: UUID, status: String, createdSince: DateTime, updatedSince: DateTime): [ReportType] podcast(id: UUID, organisationId: UUID): PodcastType podcasts(limit: Int, organisationId: UUID, all: Boolean, offset: Int): [PodcastType] podcastSearch(text: String): String feedCandidate(url: String!): String enhancement(id: UUID): EnhancementJobType enhancements(limit: Int, organisationId: UUID): [EnhancementJobType] user(id: UUID): UserType featureFlags: [FeatureFlagType] prices: String pricesPreview(productIds: [String]!, currency: String!): String organisations: [OrganisationType] organisation(id: UUID): OrganisationType teams(organisationId: UUID): [UserType] retrieveInvite(email: String, inviteToken: String): UserType invoice(transactionId: String!): String apiKeys: [ApiKeyType] } """ An article on the public Audio Audit blog. Needs no authentication: `allPosts` returns published articles only, but `post` will return a single unpublished draft to anyone who knows its id or slug. """ type PostType { id: UUID! createdAt: DateTime updatedAt: DateTime createdBy: UserType updatedBy: UserType title: String! slug: String! status: BlogPostStatusChoices! metaDescription: String content: String! """ For including JS and similar at bottom of the page. Will not be run through Markdown processor. """ nonVisibleContent: String! intro: String updatedAtCombined: String absoluteUrl: String contentFormatted: String headerImage: String category: String } """ Leverages the internal Python implementation of UUID (uuid.UUID) to provide native UUID objects in fields, resolvers and input. """ scalar UUID """ The `DateTime` scalar type represents a DateTime value as specified by [iso8601](https://en.wikipedia.org/wiki/ISO_8601). """ scalar DateTime """ A person's account, and a billable entity in its own right: every user has a personal workspace with its own plan, credit balance and podcasts, alongside any organisation they belong to. """ type UserType { password: String! lastLogin: DateTime """ Designates that this user has all permissions without explicitly assigning them. """ isSuperuser: Boolean! firstName: String! lastName: String! """Designates whether the user can log into this admin site.""" isStaff: Boolean! """ Designates whether this user should be treated as active. Unselect this instead of deleting accounts. """ isActive: Boolean! dateJoined: DateTime! id: UUID! paddleCustomerId: String monthlyCreditsOverride: Int """ Overrides the plan max_users for this entity. Null = use the plan limit. """ memberLimitOverride: Int """ Overrides the plan max_podcasts for this entity. Null = use the plan limit. """ podcastLimitOverride: Int preferredCurrency: String email: String! displayEmail: String! emailUnsubscribeToken: String emailVerified: Boolean! passwordSet: Boolean! inviteToken: String inviteTokenCreatedAt: DateTime magicLinkToken: String magicLinkTokenCreatedAt: DateTime emailNotificationBilling: Boolean! emailNotificationReportCreationMe: Boolean! emailNotificationReportCreationOthers: Boolean! emailNotificationReportCreationFeed: Boolean! emailNotificationProductAnnouncements: Boolean! emailNotificationPromotions: Boolean! """The organisations this user is a member of.""" organisations: [OrganisationType] apiKeys: [ApiKeyType!]! podcasts: [PodcastUserType!]! reports: [ReportType!]! planSet: [PlanType!]! """ JSON: the older audio-duration quota for this user's personal workspace (allowance, used and remaining milliseconds, and when the period ends). Superseded by `credits`. """ quota: String """ JSON: the personal workspace credit position — current balance, the monthly plan allowance and how much of it is used, the separately-expiring top-up balance, and when the allowance next resets. """ credits: String """ JSON: the personal workspace's live Paddle subscription, or null if there is none. Read from Paddle when requested, not from our database. """ subscription: String """ JSON: the plan behind the live Paddle subscription, or null on the free tier. Read from Paddle when requested. """ plan: String """ JSON: payment history for the live Paddle subscription, or null if there is none. Read from Paddle when requested. """ payments: String """ JSON: the podcast cap and current usage for the personal workspace. A personal workspace has no seats, so the seat keys are null. Triggers a live Paddle plan lookup, so avoid requesting it from a query that runs on every page. """ limits: String """ Gravatar URL derived from the account email, falling back to a generated identicon. Always returns a URL, whether or not the person has a Gravatar. """ avatarUrl: String """ This user's role in the named organisation — `owner`, `admin` or `member`. Null unless the caller is this user or a fellow member. Defaults to the organisation the user was reached through, so it can be omitted inside a `teams(organisationId:)` query. """ organisationRole(organisationId: UUID): String """ Workspace-scoped capabilities granted to this user on top of their role, e.g. `org.report`. Null unless the caller is this user or an admin of the organisation — plain members see roles, not spend rights. """ organisationExtraPermissions(organisationId: UUID): [String] """ True while a staff member is viewing the site as another account. A property of the calling session rather than of this user, so it reads the same on every `UserType` in a response. """ isFaked: Boolean } """ A shared workspace — a production company, studio or team — with its own members, podcasts, plan and credit balance, kept separate from any member's personal workspace. Several fields answer from the calling user's point of view rather than describing the organisation in the abstract: `role`, `isOwner` and `extraPermissions` all report on the caller's own membership. """ type OrganisationType { id: UUID! paddleCustomerId: String monthlyCreditsOverride: Int """ Overrides the plan max_users for this entity. Null = use the plan limit. """ memberLimitOverride: Int """ Overrides the plan max_podcasts for this entity. Null = use the plan limit. """ podcastLimitOverride: Int preferredCurrency: String name: String! dateJoined: DateTime! apiKeys: [ApiKeyType!]! podcasts: [PodcastOrganisationType!]! reports: [ReportType!]! """Whether the calling user owns this organisation.""" isOwner: Boolean """ The calling user's role here — `owner`, `admin` or `member`. Null if they are not a member. """ role: String """ Workspace-scoped capabilities granted to the calling user on top of their role, e.g. `org.report`. The caller's own membership only — never another member's. """ extraPermissions: [String] """ JSON: the older audio-duration quota for this workspace (allowance, used and remaining milliseconds, and when the period ends). Superseded by `credits`. """ quota: String """ JSON: this workspace's credit position — current balance, the monthly plan allowance and how much of it is used, the separately-expiring top-up balance, and when the allowance next resets. """ credits: String """ JSON: the workspace's live Paddle subscription, or null if there is none. Read from Paddle when requested, not from our database. """ subscription: String """ JSON: the plan behind the live Paddle subscription, or null on the free tier. Read from Paddle when requested. """ plan: String """ JSON: payment history for the live Paddle subscription, or null if there is none. Read from Paddle when requested. """ payments: String """ JSON: the plan seat and podcast caps with current usage, and whether they are being enforced for the calling user. Triggers a live Paddle plan lookup, so avoid requesting it from a query that runs on every page. """ limits: String } """ An API key granting programmatic access to the API on behalf of its owning workspace. The secret is not published on this type — only `maskedKey`, its first eight characters — and the `apiKeys` query lists the caller's personal keys plus those of organisations they own. """ type ApiKeyType { id: UUID! createdAt: DateTime updatedAt: DateTime userOwner: UserType organisationOwner: OrganisationType description: String! enabled: Boolean! lastUsedAt: DateTime createdBy: UserType updatedBy: UserType maskedKey: String owner: OwnerUnion } """ Whichever workspace owns a resource: a user (their personal workspace) or an organisation (a shared one). Exactly one of the two, never both — ownership is what decides whose plan and credit balance a job is billed against. """ union OwnerUnion = UserType | OrganisationType """ One organisation's link to a podcast, carrying that workspace's own settings for the show — feed monitoring, chart colour — and the roster of members attached to it. Because a podcast row is shared by feed URL, several organisations can hold independent links to the same show without seeing each other's. """ type PodcastOrganisationType { id: UUID! createdAt: DateTime updatedAt: DateTime createdBy: UserType updatedBy: UserType podcast: PodcastType! organisation: OrganisationType! automatedReporting: Boolean! lastQuotaEmail: DateTime color: String members: [PodcastOrganisationMemberType] } """ A show whose RSS feed Audio Audit polls, identified by that feed URL and shared across every workspace that has added it — so this is the show itself, not one customer's copy of it. Anything per-customer (chart colour, whether the feed is monitored, who is attached to it) lives on the `users` and `organisations` links rather than here. """ type PodcastType { id: UUID! createdAt: DateTime updatedAt: DateTime createdBy: UserType updatedBy: UserType feedUrl: String! title: String lastCheck: DateTime """Last time the feed returned a parseable RSS document""" lastSuccessfulCheck: DateTime """Consecutive failed feed checks; reset to 0 on any success""" consecutiveFailures: Int! """ Hex colour used for this show in charts, including the leading `#`. Resolves to the calling workspace's override if it set one, then the show's own colour, then one derived from its id — it is never null. """ color: String """ Channel artwork from the RSS feed, falling back to the cover image extracted from the newest report until the feed checker has stamped one. """ coverImageUrl: String """ Consecutive checks where the feed read fine but its newest episode could not be stored; reset to 0 on any successful ingest """ consecutiveIngestFailures: Int! """Last time the newest feed item was stored, or confirmed already stored""" lastSuccessfulIngest: DateTime """ The calling user's own personal-workspace link to this show. At most one entry — other people's links are never listed. """ users: [PodcastUserType] """ Links to this show held by organisations the calling user belongs to. Other customers' links to the same show are never listed. """ organisations: [PodcastOrganisationType] """ Episodes of this show, newest first. `limit` defaults to 20; pass `offset` to page through the rest. """ episodes(limit: Int, offset: Int): [PodcastEpisodeType] """ JSON: of the five most recent episodes by publication date, those that have a report — each with its date, overall score and report id, oldest first. Episodes with no report are dropped rather than backfilled, so this can be empty while older reported episodes exist. """ historicScores: String """ JSON: the last 24 hours of feed polls — timestamp, HTTP status, request duration in milliseconds, and the report each one produced. An empty array unless some workspace has feed monitoring on for this show. """ historicFeedChecks: String """ JSON: mean feed response time and uptime fraction over 24 hours, 7 days and 28 days. An empty array unless some workspace has feed monitoring on for this show. """ feedCheckStats: String """ True once the feed has failed to return a parseable RSS document on three consecutive polls. """ feedIsBroken: Boolean """ True once three consecutive polls have read the feed successfully but failed to store its newest episode. The underlying error is deliberately not published — the fault is ours, not the feed owner's. """ ingestIsBroken: Boolean """ Whether the feed is polled and reports generated automatically for the workspace the query named — the organisation given as `organisationId`, or the calling user personally when none was given. """ automatedReporting: Boolean } """ One person's personal-workspace link to a podcast — their subscription to the show, carrying the settings that are theirs alone rather than the show's: feed monitoring on or off, and a chart colour that overrides the show's own. `PodcastType.users` only ever lists the calling user's own link, never anyone else's. """ type PodcastUserType { id: UUID! createdAt: DateTime updatedAt: DateTime createdBy: UserType updatedBy: UserType podcast: PodcastType! user: UserType! automatedReporting: Boolean! lastQuotaEmail: DateTime color: String } """ An episode the feed checker has seen (REST API plan §3.3, §4.4). Rows exist only for the items a feed check has actually observed — recent episodes, not the show's full back catalogue — so a listing over this type is "what we have seen", not "what the feed contains". The field list is explicit rather than the whole model: `downloaded`, `download_priority` and `last_download_attempt` are our own scheduling bookkeeping, and the reverse relations to reports and feed checks belong to the resources that own them. """ type PodcastEpisodeType { id: UUID! createdAt: DateTime audioUrl: String! guid: String pubDate: Date episodeTitle: String } """ The `Date` scalar type represents a Date value as specified by [iso8601](https://en.wikipedia.org/wiki/ISO_8601). """ scalar Date """ One workspace member attached to one of that workspace's podcasts, carrying both their per-show access grant (`role`: `manager` or `viewer`) and their per-show report-email preference — the roster is the recipient list, so a show with nobody attached emails nobody. Names, roles and permissions are readable by anyone who can see the show; the email-status fields resolve to null for everyone but managers of the show and the member themselves. """ type PodcastOrganisationMemberType { id: UUID! createdAt: DateTime updatedAt: DateTime createdBy: UserType updatedBy: UserType podcastOrganisation: PodcastOrganisationType! user: UserType! emailReports: Boolean role: String feedEmailsPaused: Boolean emailVerified: Boolean extraPermissions: [String] } """ One audio analysis: an uploaded file or a podcast episode, the checks that ran against it, and everything derived from it — measurements, waveform, transcript, artwork. `report(id:)` looks one up by UUID without authentication, by design, so that a finished report can be shared by link; `reports` lists only what the caller's own workspace owns. """ type ReportType { start: DateTime end: DateTime status: AudioReportStatusChoices! statusChanged: DateTime! isRemoved: Boolean! id: UUID! createdAt: DateTime updatedAt: DateTime createdBy: UserType updatedBy: UserType originalFilename: String! storage: AudioReportStorageChoices! artifactsComplete: Boolean! measurementsComplete: Boolean! estimatedTimeExceeded: Boolean! podcastEpisode: PodcastEpisodeType user: UserType organisation: OrganisationType personalReport: Boolean! startedAt: DateTime """ Higher numbers = higher priority. Auto-calculated based on payment status and creation method. """ priority: Int! """ Number of times the report has been auto-retried by the queue scheduler after exceeding MAX_PROCESSING_MS. """ retryCount: Int! reportTemplate: ReportTemplateType sourceEnhancement: EnhancementJobType """Analysis credits debited at completion. Null until charged.""" creditsCharged: Decimal checksRun: [CheckTypeType!]! """ Display name for the report: the title tag read from the audio, else the podcast episode title, else the uploaded filename. """ name: String """ JSON: the headline summary — how many checks passed and the overall percentage, plus an estimate of the milliseconds remaining while the report is still running. """ stats: String """URL of the analysed audio file.""" audioUrl: String """ JSON: the artwork embedded in the audio — `url`, `width`, `height`. An empty object if the file carried none. """ coverImage: String """ URL of the rendered waveform image, or null until that artifact has completed. """ waveform: String """ JSON: the tags read from the audio file (title, album, artist and so on). An empty object if the file carried none. """ metadata: String """ JSON: what ffprobe found — duration in milliseconds, codec, channels, sample rate. An empty object until that artifact has completed. """ fileInfo: String """ JSON: one entry per check that ran, with its measured value, whether it passed, and the warning text shown when it did not. """ results: String """ JSON: the speech-to-text transcript as a list of `start`/`end`/`text` phrases, with times in milliseconds. The JSON literal `null` when the report has no completed transcription. """ transcript: String """ The show this report was generated for, when it came from a monitored feed. Null for a direct upload. """ podcast: PodcastType } """An enumeration.""" enum AudioReportStatusChoices { """pending""" PENDING """scheduled""" SCHEDULED """in_progress""" IN_PROGRESS """complete""" COMPLETE """failed""" FAILED """out_of_quota""" OUT_OF_QUOTA } """An enumeration.""" enum AudioReportStorageChoices { """local""" LOCAL """gcloud""" GCLOUD } """ A named set of checks that decides what a report runs and therefore what it costs. The `reportTemplates` query lists the ownerless system templates for everyone, plus the caller's own and — given an `organisationId` they belong to — that workspace's; the `enhancements` list is a UI suggestion only, each one selected becoming a separate, separately-billed job rather than part of the report charge. """ type ReportTemplateType { id: UUID! name: String! slug: String! """False for system-provided templates, True for customer-created ones.""" isCustom: Boolean! """The system default template used when none is specified.""" isDefault: Boolean! checks: [CheckTypeType!]! """ Suggested enhancements shown in the UI. Not billed with the report — each spawns a separate Enhancement job. """ enhancements: [EnhancementTypeType!]! destinationName: String checkIds: [UUID] enhancementIds: [UUID] } """ One quality check the analysis can run against an episode — loudness, true peak, noise floor, leading and trailing silence and so on. `creditRate` is only the marginal per-audio-minute cost of adding this check; the artifacts it needs are priced separately on `ArtifactTypeType`, and `requiredArtifactIds` resolves those dependencies transitively so a caller can price a selection without parsing `dependencies`. """ type CheckTypeType { id: UUID! name: String! verboseName: String! ordering: Int! measurementUnit: String! """ Comma-separated list of artifacts or measurements that need to complete first, in the format 'artifact.transcription' """ dependencies: String! creditRate: Float requiredArtifactIds: [UUID] } """ An audio enhancement offered as a billable job in its own right — loudness normalisation, noise reduction, compression, metadata. Listed only to callers for whom the `enhancements` feature flag is on, and an entry with `isAvailable: false` is advertised but cannot yet be run. """ type EnhancementTypeType { id: UUID! name: String! verboseName: String! description: String! ordering: Int! """ When False the enhancement is shown but cannot be selected/processed yet. """ isAvailable: Boolean! creditRate: Float suggestedByChecks: [String] } """ A combined enhancement job (mirrors ReportType). ``types`` is exposed as plain name/id lists to avoid double-registering the EnhancementType model (the billing schema already owns EnhancementTypeType). """ type EnhancementJobType { status: AudioEnhancementStatusChoices! id: UUID! createdAt: DateTime originalFilename: String! personalReport: Boolean! startedAt: DateTime """ Credits debited at completion for this enhancement. Null until charged. """ creditsCharged: Decimal """ Names of the enhancement types run in this combined job, e.g. `noise_reduction`. """ typeNames: [String] """ Ids of the enhancement types run in this combined job, matching `enhancementTypes`. """ typeIds: [UUID] """ JSON: the options chosen for each selected enhancement — loudness preset, noise-reduction strength, compression amount, metadata tags and cover. An empty object if none were given. """ parameters: String """ JSON: the run record written by the processor — what it measured, what ran and why, and per-stage timings. An empty object until the job has produced one. """ results: String """ JSON: what ffprobe found in the input file — duration in milliseconds, codec, channels, sample rate. An empty object until the processor has read it. """ fileInfo: String """URL of the enhanced audio file. Null until the job reaches `complete`.""" outputUrl: String """ The report whose audio this job was started from, when it was created from an existing analysis rather than an upload. Null otherwise. """ sourceReportId: UUID } """An enumeration.""" enum AudioEnhancementStatusChoices { """pending""" PENDING """scheduled""" SCHEDULED """in_progress""" IN_PROGRESS """complete""" COMPLETE """failed""" FAILED """out_of_quota""" OUT_OF_QUOTA } """The `Decimal` scalar type represents a python Decimal.""" scalar Decimal """ A subscription tier as sold on the pricing page: its monthly credit allowance and its seat and podcast caps. Public and unfiltered — `allPlans` needs no authentication and returns every plan row, including any not currently on sale. """ type PlanType { id: UUID! name: String! slug: String! description: String! ordering: Int! paddleSubscriptionId: Int! """ Paddle price or product id used to map an active subscription to this plan. """ paddlePriceId: String! """ Credits granted each monthly billing period. Resets monthly, no rollover. """ monthlyCredits: Int! """ Maximum members per organisation on this plan, including pending invites. Null = unlimited. Ignored for personal workspaces. """ maxUsers: Int """Maximum podcasts linked to a workspace on this plan. Null = unlimited.""" maxPodcasts: Int conversionHours: Float } """An enumeration.""" enum BlogPostStatusChoices { """Draft""" DRAFT """Published""" PUBLISHED } """ A one-off credit top-up bought outside a subscription, for work that overruns the monthly allowance. Credits granted this way expire twelve months after purchase rather than at the end of the billing period. Public; only packs currently on sale are listed. """ type CreditPackType { id: UUID! name: String! slug: String! description: String! """Credits granted when this pack is purchased.""" credits: Int! """Paddle one-time price id for this pack.""" paddlePriceId: String! """Human-readable price, e.g. "$29".""" priceDisplay: String! ordering: Int! isActive: Boolean! } """ A derived output the analysis pipeline can produce for a report — waveform, transcription, cover image, file info. Most exist only as check dependencies; only those flagged `isUserSelectable` can be asked for as a deliverable in their own right. `creditRate` is per audio-minute and is charged once per report however many checks depend on it. """ type ArtifactTypeType { id: UUID! name: String! verboseName: String! """ Whether customers can select this artifact directly as a report deliverable (e.g. a transcript), rather than it only being pulled in as a check dependency. """ isUserSelectable: Boolean! creditRate: Float } """ One movement in a workspace's credit ledger, and an immutable record once written. `amount` is signed — negative for charges and expiries — and `balanceAfter` is the workspace's whole balance immediately after it. The category of movement is published as `kind`, not `type`. Readable only by the workspace whose ledger it belongs to. """ type CreditTransactionType { created: DateTime! id: UUID! description: String! kind: String amount: Float balanceAfter: Float reportId: UUID enhancementId: UUID } """ The effective on/off state of one feature flag for whoever is asking, so a client can hide a feature that is switched off or still staff-only. Readable without authentication: anonymous callers get the full list of flags, with everything not generally available resolving to `false`. """ type FeatureFlagType { key: String enabled: Boolean } """ The write surface of the Audio Audit API, composed from the accounts, audio, billing and metrics schemas. Almost every mutation reports failure in its payload — `ok: false` with a human-readable `error` — rather than raising, so a response without a top-level `errors` block does not on its own mean the write succeeded. """ type Mutation { """ The "How did you hear about us?" answer collected during onboarding. Lands on the row registration created, but get-or-creates so an account that predates attribution capture can still answer. """ attributionSelfReport(source: String!): AttributionSelfReport """ Returns the Paddle one-time checkout parameters for a credit pack. The actual credit grant happens later, from the verified transaction.completed webhook. """ purchaseCreditPack(organisationId: UUID, packId: UUID, slug: String): PurchaseCreditPack """Create or update a customer-owned custom report template.""" saveReportTemplate(checkIds: [UUID]!, enhancementIds: [UUID], id: UUID, name: String!, organisationId: UUID): SaveReportTemplate leaveFeedback(content: String, reportId: UUID!, starRating: Int): LeaveFeedback getUploadUrl(duration: Int!, fileName: String!, fileType: String!): GetUploadUrl startReport(durationMs: Float, organisationId: UUID, originalFilename: String, reportId: UUID!, reportTemplateId: UUID): StartReport reportFromFeed(itemNum: Int, organisationId: UUID, url: String!): ReportFromFeed retryReport(reportId: UUID!): RetryReport """ Mint an Enhancement id and a signed PUT URL for its input (and, when metadata+cover is coming, the cover source). Mirrors GetUploadUrl. """ getEnhancementUploadUrl(coverFileName: String, coverFileType: String, fileName: String!, fileType: String!): GetEnhancementUploadUrl """ Create and queue a combined enhancement job from an uploaded file or a completed report's audio (server-side blob copy — no re-upload). """ startEnhancement(durationMs: Float, enhancementId: UUID, enhancementTypeIds: [UUID]!, organisationId: UUID, originalFilename: String, parameters: String, sourceReportId: UUID): StartEnhancement podcast(addOrganisationId: UUID, addUser: Boolean, automatedReporting: Boolean, automatedReportingOrganisationId: UUID, color: String, feedUrl: String, id: UUID, organisationId: UUID, removeOrganisationId: UUID, removeUser: Boolean, title: String): PodcastMutation """ Per-podcast report-email routing for a workspace (plan §5.4). Deliberately separate from PodcastMutation: the roster is its own concern and that mutation is already an eleven-argument if/elif chain. """ podcastNotifications(organisationId: UUID!, podcastId: UUID!, removeUserIds: [UUID], setMembers: [PodcastMemberInput], setMyEmailReports: Boolean): PodcastNotifications register(captchaToken: String!, email: String!, firstName: String!, id: UUID, inviteToken: String, lastName: String!, password: String!): Register lookupUser(email: String, id: UUID): LookupUser changePlan(organisationId: UUID, priceId: String!): ChangePlanMutation cancelPlan(organisationId: UUID): CancelPlanMutation setPaddleCustomerId(currency: String, organisationId: UUID, paddleCustomerId: String!): SetPaddleCustomerIdMutation tokenAuth(email: String!, password: String!): CustomObtainJSONWebToken verifyToken(token: String): Verify refreshToken(refreshToken: String): Refresh organisation(id: UUID, name: String): OrganisationMutation organisationDelete(organisationId: UUID!): OrganisationDeleteMutation user(email: String, emailNotificationBilling: Boolean, emailNotificationProductAnnouncements: Boolean, emailNotificationPromotions: Boolean, emailNotificationReportCreationFeed: Boolean, emailNotificationReportCreationMe: Boolean, emailNotificationReportCreationOthers: Boolean, firstName: String, id: UUID!, lastName: String): UserMutation userInvite(email: String!, firstName: String, lastName: String, organisationId: UUID!, podcasts: [PodcastAssignmentInput], role: String, sendReportEmails: Boolean): UserInviteMutation userUninvite(organisationId: UUID!, userId: UUID!): UserUninviteMutation userEmailVerify(email: String!, token: String!): UserEmaiVerifyMutation leaveOrganisation(organisationId: UUID!): LeaveOrganisationMutation setOrganisationRole(organisationId: UUID!, role: String!, userId: UUID!): SetOrganisationRoleMutation """ The org-scoped overrides on one member's row — `org.report`/`org.enhance` (plan §5.4, §9 decision 19) and `org.api_keys` (REST plan §4.13, which no role preset but Owner's grants, and which only an Owner may hand out). Podcast-shaped extras travel through `podcastNotifications` instead; the save-time validator polices scope, vocabulary and redundancy (an Admin+ target's preset already grants the spend pair, so writing it there fails naturally). """ setOrganisationMemberPermissions(extraPermissions: [String]!, organisationId: UUID!, userId: UUID!): SetOrganisationMemberPermissionsMutation getUpdatePaymentMethodTransaction(subscriptionId: String!): GetUpdatePaymentMethodTransaction sendMagicLink(email: String!): SendMagicLink verifyMagicLink(email: String!, magicToken: String!): VerifyMagicLink createApiKey(description: String!, organisationId: UUID): CreateApiKeyMutation updateApiKey(description: String, enabled: Boolean, id: UUID!): UpdateApiKeyMutation deleteApiKey(id: UUID!): DeleteApiKeyMutation } """ The "How did you hear about us?" answer collected during onboarding. Lands on the row registration created, but get-or-creates so an account that predates attribution capture can still answer. """ type AttributionSelfReport { ok: Boolean error: String } """ Returns the Paddle one-time checkout parameters for a credit pack. The actual credit grant happens later, from the verified transaction.completed webhook. """ type PurchaseCreditPack { ok: Boolean error: String checkout: String } """Create or update a customer-owned custom report template.""" type SaveReportTemplate { ok: Boolean error: String template: ReportTemplateType } type LeaveFeedback { ok: Boolean error: String } type GetUploadUrl { ok: Boolean error: String reportId: UUID url: String } type StartReport { ok: Boolean error: String status: String estimatedCredits: Int reportId: UUID } type ReportFromFeed { ok: Boolean reportId: UUID error: String status: String } type RetryReport { ok: Boolean error: String } """ Mint an Enhancement id and a signed PUT URL for its input (and, when metadata+cover is coming, the cover source). Mirrors GetUploadUrl. """ type GetEnhancementUploadUrl { ok: Boolean error: String enhancementId: UUID url: String coverUrl: String } """ Create and queue a combined enhancement job from an uploaded file or a completed report's audio (server-side blob copy — no re-upload). """ type StartEnhancement { ok: Boolean error: String status: String enhancementId: UUID estimatedCredits: Int } type PodcastMutation { ok: Boolean id: UUID error: String } """ Per-podcast report-email routing for a workspace (plan §5.4). Deliberately separate from PodcastMutation: the roster is its own concern and that mutation is already an eleven-argument if/elif chain. """ type PodcastNotifications { ok: Boolean error: String podcast: PodcastType } input PodcastMemberInput { userId: UUID! role: String extraPermissions: [String] } type Register { ok: Boolean error: String user: UserType } type LookupUser { ok: Boolean error: String userId: String } type ChangePlanMutation { ok: Boolean error: String } type CancelPlanMutation { ok: Boolean } type SetPaddleCustomerIdMutation { ok: Boolean } type CustomObtainJSONWebToken { payload: GenericScalar! refreshExpiresIn: Int! token: String! refreshToken: String! } """ The `GenericScalar` scalar type represents a generic GraphQL scalar value that could be: String, Boolean, Int, Float, List or Object. """ scalar GenericScalar type Verify { payload: GenericScalar! } type Refresh { payload: GenericScalar! refreshExpiresIn: Int! token: String! refreshToken: String! } type OrganisationMutation { ok: Boolean id: UUID error: String } type OrganisationDeleteMutation { ok: Boolean error: String } type UserMutation { ok: Boolean id: UUID error: String } type UserInviteMutation { ok: Boolean id: UUID error: String } input PodcastAssignmentInput { podcastId: UUID! role: String } type UserUninviteMutation { ok: Boolean error: String } type UserEmaiVerifyMutation { ok: Boolean } type LeaveOrganisationMutation { ok: Boolean } type SetOrganisationRoleMutation { ok: Boolean error: String } """ The org-scoped overrides on one member's row — `org.report`/`org.enhance` (plan §5.4, §9 decision 19) and `org.api_keys` (REST plan §4.13, which no role preset but Owner's grants, and which only an Owner may hand out). Podcast-shaped extras travel through `podcastNotifications` instead; the save-time validator polices scope, vocabulary and redundancy (an Admin+ target's preset already grants the spend pair, so writing it there fails naturally). """ type SetOrganisationMemberPermissionsMutation { ok: Boolean error: String } type GetUpdatePaymentMethodTransaction { ok: Boolean id: String } type SendMagicLink { ok: Boolean error: String } type VerifyMagicLink { ok: Boolean error: String token: String refreshToken: String refreshExpiresIn: Int payload: GenericScalar user: UserType } type CreateApiKeyMutation { ok: Boolean apiKey: ApiKeyWithKeyType error: String key: String } """ An API key together with its full secret `key`, returned only by the `createApiKey` mutation — everywhere else a key appears, only the masked form is published. Capture the value at creation; a lost key has to be replaced, not recovered. """ type ApiKeyWithKeyType { id: UUID description: String enabled: Boolean key: String owner: OwnerUnion } type UpdateApiKeyMutation { ok: Boolean apiKey: ApiKeyType error: String } type DeleteApiKeyMutation { ok: Boolean error: String }