@workwae/sdk 0.1.0-alpha.3 → 0.1.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1 +1 @@
1
- import*as z3 from"zod";var MAX_EMAIL_LENGTH=320,MAX_NAME_LENGTH=100,MAX_EXTERNAL_REF_LENGTH=255,MAX_DISPLAY_NAME_LENGTH=255,MAX_ADDRESS_NAME_LENGTH=256,MAX_SECRET_LENGTH=1024,MAX_NOTE_LENGTH=500,MAX_FILTER_QUERY_LENGTH=200,MAX_URL_LENGTH=2048,MAX_TIME_ZONE_LENGTH=64,MAX_CURSOR_LENGTH=512,MAX_PAGE_SIZE=200,DEFAULT_PAGE_SIZE=50,MAX_PROVIDER_ID_LENGTH=1024,MAX_HOST_LENGTH=253,MAX_PORT=65535,MAX_STATE_LENGTH=1024,MIN_TOKEN_TTL_SECONDS=60,MAX_TOKEN_TTL_SECONDS=86400,MAX_MCP_KEYS_PER_ACCOUNT=25,MAX_REDIRECT_URIS=50,MAX_UNDO_SEND_SECONDS=60,MIN_EVENT_RETENTION_DAYS=7,MAX_EVENT_RETENTION_DAYS=30,MAX_MCP_ALLOWED_DOMAINS=100,MAX_MCP_DAILY_SEND_LIMIT=1e4;var MAX_SUBJECT_LENGTH=998,MAX_MESSAGE_ID_LENGTH=998,MAX_BODY_LENGTH=2097152,MAX_RECIPIENTS=500,MAX_REPLY_TO=10,MAX_ATTACHMENTS=50,MAX_FILENAME_LENGTH=255,MAX_CONTENT_TYPE_LENGTH=255,MAX_CONTENT_ID_LENGTH=255,MAX_SEND_REFERENCE_LENGTH=255,MAX_SEND_WAIT_SECONDS=30,MAX_SEND_HEADERS=50,MAX_HEADER_VALUE_LENGTH=4096,MAX_CALENDAR_LENGTH=262144,MAX_MAILBOX_CHANGES=50,MAX_SEARCH_QUERY_LENGTH=500,MAX_SEARCH_PAGE_SIZE=100,DEFAULT_SEARCH_PAGE_SIZE=25,MAX_THREAD_PAGE_SIZE=100,DEFAULT_THREAD_PAGE_SIZE=25,MAX_MESSAGE_BODIES_PAGE_SIZE=25;var MAX_SIGNATURE_HTML_LENGTH=1e5,MAX_SIGNATURE_TEXT_LENGTH=20000,MAX_TEMPLATE_NAME_LENGTH=255,MAX_TEMPLATE_DESCRIPTION_LENGTH=1000,MAX_TEMPLATE_SUBJECT_LENGTH=2000,MAX_TEMPLATE_PREVIEW_TEXT_LENGTH=2000,MAX_TEMPLATE_BODY_LENGTH=1e6,MAX_MAIL_MERGE_ENTRIES=100,MAX_MAIL_MERGE_ATTACHMENT_BYTES=52428800,MAX_RENDER_PARAMS_BYTES=262144,MAX_RENDER_ITERATIONS=1e4,MAX_MARKDOWN_LENGTH=1e5,MAX_SUPPRESSION_BATCH=1000,MAX_SUPPRESSION_LIST_DESCRIPTION_LENGTH=500,MAX_SUPPRESSION_REASON_LENGTH=200,MAX_EVENT_ATTENDEES=500,MAX_EVENT_SUBJECT_LENGTH=255,MAX_EVENT_LOCATION_LENGTH=255,MAX_EVENT_BODY_LENGTH=1e6,MAX_EVENT_COMMENT_LENGTH=2000,MAX_RECURRENCE_LINES=20,MAX_RECURRENCE_LINE_LENGTH=1000,MAX_REMINDER_MINUTES=40320,MAX_AVAILABILITY_ATTENDEES=20,MAX_AVAILABILITY_DAYS=62,MIN_SLOT_MINUTES=5,MAX_SLOT_MINUTES=1440,MAX_SUGGESTIONS=100,MAX_AVAILABILITY_ACCOUNTS=20,MAX_EVENT_RANGE_DAYS=400,MAX_CALENDAR_EVENT_PAGE_SIZE=500,DEFAULT_CALENDAR_EVENT_PAGE_SIZE=100,MAX_SCHEDULING_DAY_WINDOWS=8,MAX_SCHEDULING_WINDOW_DAYS=365,MAX_SCHEDULING_BUFFER_MINUTES=1440,MAX_SCHEDULING_NOTICE_MINUTES=86400,MAX_SCHEDULING_PER_DAY=1000,MAX_SCHEDULING_TITLE_LENGTH=255,MAX_SCHEDULING_DESCRIPTION_LENGTH=1e4,MAX_BOOKING_NOTES_LENGTH=2000,MAX_SCHEDULING_SLOTS=1000,DEFAULT_SCHEDULING_SLOTS=200,MAX_CONTACT_ENTRIES=20,MAX_CONTACT_FIELD_LENGTH=256,MAX_CONTACT_PHONE_LENGTH=64,MAX_CONTACT_NOTES_LENGTH=1e4,MAX_CONTACT_PAGE_SIZE=500,DEFAULT_CONTACT_PAGE_SIZE=100,MAX_WEBHOOK_FILTER_VALUES=100,MAX_SUBJECT_FILTER_LENGTH=200,MAX_REPLAY_EVENTS=1000,MAX_WEBHOOK_ATTEMPT_PAGE_SIZE=500,DEFAULT_WEBHOOK_ATTEMPT_PAGE_SIZE=50,MAX_SIGNATURE_CANDIDATES=8,DEFAULT_WEBHOOK_SECRET_GRACE_SECONDS=86400,MAX_WEBHOOK_SECRET_GRACE_SECONDS=604800,MAX_DRAFT_INSTRUCTIONS_LENGTH=2000,MAX_DRAFT_CONTEXT_LENGTH=20000,MAX_EMAIL_VALIDATIONS=10,MAX_MEETING_APP_ID_LENGTH=200,MAX_REFRESH_TOKEN_LENGTH=4096,MAX_KEY_FILE_LENGTH=20000,MAX_DIRECTORY_QUERY_LENGTH=100,MAX_DIRECTORY_PAGE_SIZE=200,DEFAULT_DIRECTORY_PAGE_SIZE=100,MAX_ADD_MAILBOXES=100,MAX_ENTRA_ID_LENGTH=100;var MAX_VALIDATION_CREDITS=1e9;var MAX_CREDIT_PACK_CREDITS=1e7,MAX_CREDIT_PACKS_PER_PLAN=5;var MAX_USAGE_RANGE_DAYS=366;import*as z from"zod";var openApiComponents=new Map,componentNames=new WeakMap;function named(name,schema){if(openApiComponents.has(name))throw Error(`OpenAPI component ${name} registered twice`);return openApiComponents.set(name,schema),componentNames.set(schema,name),schema}function componentName(schema,wrapper){let name=componentNames.get(schema);if(name===void 0)throw Error(`${wrapper}() of a schema that is not named()`);return name}var Id=z.uuid(),ProviderId=z.string().min(1).max(MAX_PROVIDER_ID_LENGTH),EventId=named("EventId",z.string().regex(/^[0-9]{1,19}$/,"Expected an event id")),Cursor=z.string().min(1).max(MAX_CURSOR_LENGTH),Email=z.email().max(MAX_EMAIL_LENGTH),Timestamp=z.preprocess((value)=>typeof value==="string"?new Date(value):value,z.date()),QueryTimestamp=z.preprocess((value)=>typeof value==="string"?new Date(value):value,z.date()),DateOnly=z.iso.date();function trimmed(max){return z.string().trim().min(1).max(max)}var Name=z.string().min(1).max(MAX_NAME_LENGTH),ExternalRef=z.string().max(MAX_EXTERNAL_REF_LENGTH),Address=named("Address",z.object({name:z.string().nullable(),address:z.string()})),AddressInput=named("AddressInput",z.object({name:z.string().max(MAX_ADDRESS_NAME_LENGTH).optional(),address:Email})),QueryBoolean=z.union([z.boolean(),z.enum(["true","false"])]).transform((v)=>v===!0||v==="true"),pageFields=(item)=>({items:z.array(item),nextCursor:Cursor.nullable()}),Page=(item)=>named(`${componentName(item,"Page")}Page`,z.object(pageFields(item)));async function*followCursor(fetch2,cursor,next){let at=cursor;for(;;){let page=await fetch2(at);if(yield page,at=next(page),at===void 0)return}}var listFields=(item)=>({items:z.array(item)}),List=(item)=>named(`${componentName(item,"List")}List`,z.object(listFields(item)));var DAY_MS=86400000;function instant(value){if(value instanceof Date)return value.getTime();if(typeof value!=="string")return null;let ms=Date.parse(value);return Number.isNaN(ms)?null:ms}function withDateRange(schema,range){return schema.superRefine((value,ctx)=>{let from=instant(value[range.from]),to=instant(value[range.to]);if(from===null||to===null)return;let span=to-from,params={reason:"invalid_range"};if(span<0||span===0&&!range.inclusive)ctx.addIssue({code:"custom",path:[range.to],message:`${range.to} must be ${range.inclusive?"on or after":"after"} ${range.from}`,params});else if(range.maxSpanMs!==void 0&&span>range.maxSpanMs)ctx.addIssue({code:"custom",path:[range.to],message:range.maxSpanMs%DAY_MS===0?`The range may span at most ${range.maxSpanMs/DAY_MS} days`:`The range may span at most ${range.maxSpanMs} ms`,params})})}function pageLimit(max,fallback){return z.coerce.number().int().min(1).max(max).default(fallback)}var Limit=pageLimit(MAX_PAGE_SIZE,DEFAULT_PAGE_SIZE),Paged={limit:Limit,cursor:Cursor.optional()},AccountIdInput=named("AccountIdInput",z.object({accountId:Id})),StreamedFile=z.custom((value)=>value instanceof ReadableStream||value instanceof Blob).transform((stream)=>stream);var publicOperation=(operation)=>({...operation,security:[]});function fileResponse(file){return(operation)=>{let ok=operation.responses?.["200"];if(!ok||"$ref"in ok)throw Error(`OpenAPI: no 200 response for ${operation.operationId}`);return{...operation,responses:{...operation.responses,200:{...ok,description:file.description,content:{[file.mediaType]:{schema:{type:"string",contentMediaType:file.mediaType}}},headers:{"content-disposition":{description:`Always \`${file.disposition}; filename="..."\``,schema:{type:"string"}},...file.headers}}}}}}import*as z2 from"zod";var BodyStorage=named("BodyStorage",z2.enum(["none","cache","full"])),EffectiveSettings=named("EffectiveSettings",z2.object({bodyStorage:BodyStorage,syncWindowDays:z2.number().int().positive().nullable(),mailEnabled:z2.boolean(),calendarEnabled:z2.boolean(),contactsEnabled:z2.boolean(),undoSendSeconds:z2.number().int().min(0).max(MAX_UNDO_SEND_SECONDS),trackOpens:z2.boolean(),trackClicks:z2.boolean(),trackingHoneypot:z2.boolean(),suppressionCheck:z2.enum(["lists","always"]),sendsPerMinute:z2.number().int().min(1).max(600).nullable()})),SettingsOverride=named("SettingsOverride",EffectiveSettings.partial());var OptOutHandling=named("OptOutHandling",z2.enum(["off","review","suppress"])),McpSendMode=named("McpSendMode",z2.enum(["allow","confirm","drafts_only"])),RecipientDomain=z2.string().trim().toLowerCase().regex(/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9-]{2,63}$/,'Expected a domain name such as "example.com"'),McpSendPolicy=named("McpSendPolicy",z2.object({mode:McpSendMode,allowedRecipientDomains:z2.array(RecipientDomain).min(1).max(MAX_MCP_ALLOWED_DOMAINS).nullable(),dailySendLimit:z2.number().int().min(1).max(MAX_MCP_DAILY_SEND_LIMIT).nullable(),externalRecipientConfirmation:z2.boolean()})),McpSendPolicyPatch=named("McpSendPolicyPatch",McpSendPolicy.partial()),TenantSettings=named("TenantSettings",EffectiveSettings.extend({optOutHandling:OptOutHandling,eventRetentionDays:z2.number().int().min(MIN_EVENT_RETENTION_DAYS).max(MAX_EVENT_RETENTION_DAYS),redirectUris:z2.array(z2.url()).max(MAX_REDIRECT_URIS),aiReplyClassification:z2.boolean(),mcpSendPolicy:McpSendPolicy})),UpdateTenantSettings=named("UpdateTenantSettings",TenantSettings.partial().extend({mcpSendPolicy:McpSendPolicyPatch.optional()}));var OAuthProvider=named("OAuthProvider",z3.enum(["google","microsoft"])),MicrosoftCloud=named("MicrosoftCloud",z3.enum(["global","gcc-high","dod","china"])),AccountProvider=named("AccountProvider",z3.enum(["gmail","graph","imap"])),AccountStatus=named("AccountStatus",z3.enum(["active","paused","reauth_required","disabled"])),SettableAccountStatus=named("SettableAccountStatus",AccountStatus.extract(["active","paused"])),RuntimeState=named("RuntimeState",z3.enum(["pending","connecting","connected","error","stopped"])),AccountRuntime=named("AccountRuntime",z3.object({state:RuntimeState,detail:z3.string().nullable(),ownerInstanceId:Id.nullable(),updatedAt:Timestamp})),MailSecurity=named("MailSecurity",z3.enum(["tls","starttls","none"])),MailServer=named("MailServer",z3.object({host:z3.string(),port:z3.number().int(),security:MailSecurity,user:z3.string()})),DavAuth=named("DavAuth",z3.enum(["imap_password","dav_password"])),DavSettings=named("DavSettings",z3.object({calendarUrl:z3.string().nullable(),contactsUrl:z3.string().nullable(),user:z3.string(),auth:DavAuth})),ImapAuth=named("ImapAuth",z3.enum(["password","google","microsoft"])),ImapSettings=named("ImapSettings",z3.object({imap:MailServer,smtp:MailServer.nullable(),auth:ImapAuth,appendSent:z3.boolean(),dav:DavSettings.nullable()})),AccountAuthMethod=named("AccountAuthMethod",z3.enum(["oauth","password","google_delegation","microsoft_app","shared_mailbox","google_group"])),AccountAuth=named("AccountAuth",z3.object({method:AccountAuthMethod,orgConnectionId:Id.nullable(),delegateAccountId:Id.nullable(),relayEmail:Email.nullable()})),Account=named("Account",z3.object({id:Id,externalRef:z3.string().nullable(),provider:AccountProvider,email:z3.string(),displayName:z3.string().nullable(),oauthAppId:Id.nullable(),cloud:MicrosoftCloud.nullable(),status:AccountStatus,statusReason:z3.string().nullable(),settings:EffectiveSettings,settingsOverride:SettingsOverride,runtime:AccountRuntime.nullable(),imap:ImapSettings.nullable(),auth:AccountAuth,initializedAt:Timestamp.nullable(),calendarInitializedAt:Timestamp.nullable(),contactsInitializedAt:Timestamp.nullable(),createdAt:Timestamp,updatedAt:Timestamp})),SyncKind=named("SyncKind",z3.enum(["mail","calendar","contacts"])),SyncAccountRequest=named("SyncAccountRequest",z3.object({kinds:z3.array(SyncKind).min(1).optional()})),AccountSyncQueued=named("AccountSyncQueued",z3.object({kinds:z3.array(SyncKind)})),AccountPage=Page(Account),ListAccountsQuery=named("ListAccountsQuery",z3.object({...Paged,externalRef:z3.string().optional(),q:trimmed(MAX_FILTER_QUERY_LENGTH).optional(),status:AccountStatus.optional(),orgConnectionId:Id.optional(),delegateAccountId:Id.optional()})),CreateAccount=named("CreateAccount",z3.object({provider:OAuthProvider,microsoftCloud:MicrosoftCloud.default("global"),refreshToken:z3.string().min(1),oauthAppId:Id.optional(),externalRef:ExternalRef.optional(),settings:SettingsOverride.optional(),sharedMailboxesEnabled:z3.boolean().optional()})),UpdateAccount=named("UpdateAccount",z3.object({externalRef:ExternalRef.nullable().optional(),status:SettableAccountStatus.optional(),settings:SettingsOverride.optional()})),DeleteAccountQuery=named("DeleteAccountQuery",z3.object({purge:QueryBoolean.default(!1)})),DeleteAccountInput=named("DeleteAccountInput",z3.object({params:AccountIdInput,query:DeleteAccountQuery})),MintAccountTokenRequest=named("MintAccountTokenRequest",z3.object({ttlSeconds:z3.number().int().min(MIN_TOKEN_TTL_SECONDS).max(MAX_TOKEN_TTL_SECONDS).default(900)})),AccountToken=named("AccountToken",z3.object({token:z3.string(),expiresAt:Timestamp})),MailServerInput=named("MailServerInput",z3.object({host:z3.string().min(1).max(MAX_HOST_LENGTH),port:z3.number().int().min(1).max(MAX_PORT),security:MailSecurity,user:z3.string().min(1).max(MAX_EMAIL_LENGTH).optional()})),Password=z3.string().min(1).max(MAX_SECRET_LENGTH),DavUrl=z3.url({protocol:/^https?$/}).max(MAX_URL_LENGTH),DavInput=named("DavInput",z3.object({calendarUrl:DavUrl.nullable().optional(),contactsUrl:DavUrl.nullable().optional(),user:z3.string().min(1).max(MAX_EMAIL_LENGTH).optional(),password:Password.nullable().optional()})),ImapAccountInput=named("ImapAccountInput",z3.object({email:Email,displayName:z3.string().max(MAX_DISPLAY_NAME_LENGTH).optional(),password:Password,smtpPassword:Password.optional(),imap:MailServerInput.optional(),smtp:MailServerInput.nullable().optional(),appendSent:z3.boolean().optional(),dav:DavInput.optional()})),CreateImapAccount=named("CreateImapAccount",ImapAccountInput.extend({externalRef:ExternalRef.optional(),settings:SettingsOverride.optional()})),UpdateImapSettings=named("UpdateImapSettings",z3.object({password:Password.optional(),smtpPassword:Password.nullable().optional(),imap:MailServerInput.optional(),smtp:MailServerInput.nullable().optional(),appendSent:z3.boolean().optional(),dav:DavInput.nullable().optional()})),DavDiscoverySource=named("DavDiscoverySource",z3.enum(["provider","well-known","srv"])),DiscoveredDav=named("DiscoveredDav",z3.object({source:DavDiscoverySource,calendarUrl:z3.string().nullable(),contactsUrl:z3.string().nullable()})),AutoconfigSource=named("AutoconfigSource",z3.enum(["provider","autoconfig","ispdb","srv","mx"])),DiscoveredSettings=named("DiscoveredSettings",z3.object({source:AutoconfigSource,imap:MailServer,smtp:MailServer.nullable(),oauth:OAuthProvider.nullable(),dav:DiscoveredDav.nullable()})),AutoconfigQuery=named("AutoconfigQuery",z3.object({email:Email}));import*as z4 from"zod";var AiDraftFormat=named("AiDraftFormat",z4.enum(["text","markdown"])),AiDraftContext=named("AiDraftContext",z4.object({accountId:Id.optional(),replyToMessageId:Id.optional(),currentDraft:z4.string().max(MAX_DRAFT_CONTEXT_LENGTH).optional()}).refine((c)=>!c.replyToMessageId||c.accountId,{message:"replyToMessageId needs accountId",path:["accountId"]})),CreateAiDraft=named("CreateAiDraft",z4.object({instructions:trimmed(MAX_DRAFT_INSTRUCTIONS_LENGTH),context:AiDraftContext.optional(),format:AiDraftFormat})),AiDraft=named("AiDraft",z4.object({text:z4.string()}));import*as z5 from"zod";var AuditActorType=named("AuditActorType",z5.enum(["user","apiKey","accountToken","connectSession","scimCredential"])),AuditActor=named("AuditActor",z5.object({type:AuditActorType,id:Id,label:z5.string().nullable()})),AuditSurface=named("AuditSurface",z5.enum(["v1","rpc","mcp","auth","smtp"])),AuditOutcome=named("AuditOutcome",z5.enum(["success","failure"])),AuditEntry=named("AuditEntry",z5.object({id:Id,createdAt:Timestamp,actor:AuditActor,action:z5.string(),surface:AuditSurface,targets:z5.record(z5.string(),z5.union([z5.string(),z5.array(z5.string())])),ip:z5.string().nullable(),userAgent:z5.string().nullable(),status:z5.number().int(),errorReason:z5.string().nullable(),requestId:z5.string().nullable()})),AuditEntryPage=Page(AuditEntry),ListAuditLogQuery=named("ListAuditLogQuery",withDateRange(z5.object({...Paged,actorType:AuditActorType.optional(),actorId:Id.optional(),action:z5.string().min(1).max(MAX_FILTER_QUERY_LENGTH).optional(),accountId:Id.optional(),outcome:AuditOutcome.optional(),from:QueryTimestamp.optional(),to:QueryTimestamp.optional()}),{from:"from",to:"to",inclusive:!0}));import*as z9 from"zod";import*as z8 from"zod";import*as z7 from"zod";import*as z6 from"zod";var McpKeyScope=named("McpKeyScope",z6.enum(["read","write","send"])),McpKeyScopes=z6.array(McpKeyScope).min(1),McpKey=named("McpKey",z6.object({id:Id,name:z6.string(),prefix:z6.string(),scopes:z6.array(McpKeyScope),accountId:Id,expiresAt:Timestamp.nullable(),createdAt:Timestamp,lastUsedAt:Timestamp.nullable(),sendPolicy:McpSendPolicyPatch.nullable()})),McpKeyList=List(McpKey),CreatedMcpKey=named("CreatedMcpKey",McpKey.extend({key:z6.string(),mcpUrl:z6.url()})),CreateMcpKey=named("CreateMcpKey",z6.object({name:Name.default("MCP"),scopes:McpKeyScopes,expiresAt:Timestamp.refine((at)=>at.getTime()>Date.now(),{message:"expiresAt must be in the future"}).nullable().optional(),sendPolicy:McpSendPolicyPatch.nullable().optional()})),McpKeyIdInput=named("McpKeyIdInput",AccountIdInput.extend({keyId:Id})),ConnectSessionMcp=named("ConnectSessionMcp",z6.object({scopes:McpKeyScopes,name:Name.default("MCP")}));var MailAccess=named("MailAccess",z7.enum(["api","imap"])),ConnectOptions=named("ConnectOptions",z7.object({mailEnabled:z7.boolean().optional(),calendarEnabled:z7.boolean().optional(),contactsEnabled:z7.boolean().optional(),bodyStorage:BodyStorage.optional(),mailAccess:MailAccess.optional(),sharedMailboxesEnabled:z7.boolean().optional()})),ConnectProvider=named("ConnectProvider",z7.enum(["google","microsoft","imap"])),ConnectSessionStatus=named("ConnectSessionStatus",z7.enum(["pending","completed","failed"])),ConnectErrorCode=named("ConnectErrorCode",z7.enum(["session_used","session_expired","provider_not_allowed","access_denied","access_token_rejected","provider_error","provider_unavailable","provider_not_configured","unsupported_provider","token_exchange_failed","invalid_client","interaction_required","invalid_grant","missing_refresh_token","scope_denied","identity_failed","identity_unavailable","credential_conflict","credential_missing","delegate_missing","account_exists","account_mismatch","account_not_found","unknown_oauth_app","oauth_app_mismatch","oauth_app_secret_unreadable","google_imap_requires_own_app","unknown_meeting_app","meeting_app_mismatch","meeting_app_secret_unreadable","connect_domain_inactive","consent_failed","consent_required","consent_revoked","consent_unverified","directory_already_connected","directory_mismatch","directory_unavailable","invalid_certificate","invalid_service_account_key","org_connection_not_found","org_connection_secret_unreadable","plan_limit_accounts","plan_required","spend_cap_reached","plan_module_unavailable","connect_grants_nothing","billing_past_due","internal_error"])),ConnectSessionPublic=named("ConnectSessionPublic",z7.object({tenantName:z7.string(),providers:z7.array(ConnectProvider),microsoftCloud:MicrosoftCloud,status:ConnectSessionStatus,reauthEmail:z7.string().nullable(),modules:z7.object({mail:z7.boolean(),calendar:z7.boolean(),contacts:z7.boolean()}),loginHint:z7.string().nullable(),expiresAt:Timestamp})),ConnectSession=named("ConnectSession",z7.object({id:Id,status:ConnectSessionStatus,error:ConnectErrorCode.nullable(),accountId:Id.nullable(),email:z7.string().nullable(),mcpKeyId:Id.nullable(),mcpKeySkipped:z7.enum(["mcp_key_limit"]).nullable(),expiresAt:Timestamp,createdAt:Timestamp})),RedirectState=z7.string().max(MAX_STATE_LENGTH),SessionTtlSeconds=z7.number().int().min(MIN_TOKEN_TTL_SECONDS).max(MAX_TOKEN_TTL_SECONDS).default(3600),CreateConnectSession=named("CreateConnectSession",z7.object({redirectUri:z7.url().optional(),providers:z7.array(ConnectProvider).min(1).default(["google","microsoft"]),microsoftCloud:MicrosoftCloud.default("global"),oauthAppIds:z7.object({google:Id.optional(),microsoft:Id.optional()}).optional(),externalRef:ExternalRef.optional(),loginHint:Email.optional(),state:RedirectState.optional(),accountId:Id.optional(),options:ConnectOptions.optional(),mcp:ConnectSessionMcp.optional(),ttlSeconds:SessionTtlSeconds})),SessionLink=named("SessionLink",z7.object({id:Id,url:z7.url(),expiresAt:Timestamp})),ConnectSessionIdInput=named("ConnectSessionIdInput",z7.object({sessionId:Id})),StartOAuthRequest=named("StartOAuthRequest",z7.object({provider:OAuthProvider,redirectUri:z7.url(),microsoftCloud:MicrosoftCloud.default("global"),oauthAppId:Id.optional(),externalRef:ExternalRef.optional(),state:RedirectState.optional(),accountId:Id.optional(),options:ConnectOptions.optional()})),OAuthAuthorization=named("OAuthAuthorization",z7.object({authorizeUrl:z7.url()})),SessionTokenInput=named("SessionTokenInput",z7.object({token:z7.string().min(1)})),HostedImapResult=named("HostedImapResult",z7.object({redirectUrl:z7.url()}));var MeetingService=named("MeetingService",z8.enum(["zoom","webex"])),MeetingConnectionStatus=named("MeetingConnectionStatus",z8.enum(["active","reauth_required"])),MeetingConnection=named("MeetingConnection",z8.object({service:MeetingService,email:z8.string(),displayName:z8.string().nullable(),meetingAppId:Id.nullable(),status:MeetingConnectionStatus,statusReason:z8.string().nullable(),createdAt:Timestamp,updatedAt:Timestamp})),MeetingConnectionList=List(MeetingConnection),MeetingAppKind=named("MeetingAppKind",z8.enum(["zoom_oauth","zoom_s2s","webex_oauth","webex_service_app"])),MeetingAppStatus=named("MeetingAppStatus",z8.enum(["unverified","active","failing"])),MeetingApp=named("MeetingApp",z8.object({id:Id,kind:MeetingAppKind,service:MeetingService,name:z8.string(),clientId:z8.string(),zoomAccountId:z8.string().nullable(),status:MeetingAppStatus.nullable(),statusDetail:z8.string().nullable(),checkedAt:Timestamp.nullable(),scopes:z8.array(z8.string()),useConnectDomain:z8.boolean(),redirectUri:z8.string().nullable(),createdAt:Timestamp})),MeetingAppList=List(MeetingApp),ClientId=z8.string().min(1).max(MAX_MEETING_APP_ID_LENGTH),ClientSecret=z8.string().min(1).max(MAX_SECRET_LENGTH),UseConnectDomain=z8.boolean().optional(),CreateMeetingApp=named("CreateMeetingApp",z8.discriminatedUnion("kind",[z8.object({kind:z8.literal("zoom_oauth"),name:Name,clientId:ClientId,clientSecret:ClientSecret,useConnectDomain:UseConnectDomain}),z8.object({kind:z8.literal("zoom_s2s"),name:Name,accountId:z8.string().min(1).max(MAX_MEETING_APP_ID_LENGTH),clientId:ClientId,clientSecret:ClientSecret,useConnectDomain:UseConnectDomain}),z8.object({kind:z8.literal("webex_oauth"),name:Name,clientId:ClientId,clientSecret:ClientSecret,useConnectDomain:UseConnectDomain}),z8.object({kind:z8.literal("webex_service_app"),name:Name,clientId:ClientId,clientSecret:ClientSecret,useConnectDomain:UseConnectDomain,refreshToken:z8.string().min(1).max(MAX_REFRESH_TOKEN_LENGTH)})])),UpdateMeetingApp=named("UpdateMeetingApp",z8.object({useConnectDomain:z8.boolean().optional()})),MeetingAppCheck=named("MeetingAppCheck",z8.object({name:z8.string(),isOk:z8.boolean(),detail:z8.string()})),MeetingAppTestResult=named("MeetingAppTestResult",z8.object({app:MeetingApp,checks:z8.array(MeetingAppCheck)})),MeetingConnectSessionPublic=named("MeetingConnectSessionPublic",z8.object({tenantName:z8.string(),service:MeetingService,accountEmail:z8.string(),status:ConnectSessionStatus,expiresAt:Timestamp})),MeetingAppIdInput=named("MeetingAppIdInput",z8.object({appId:Id})),TestMeetingAppRequest=named("TestMeetingAppRequest",z8.object({user:Email.optional()})),MeetingConnectionInput=named("MeetingConnectionInput",AccountIdInput.extend({service:MeetingService})),CreateMeetingConnectSession=named("CreateMeetingConnectSession",z8.object({service:MeetingService,redirectUri:z8.url().optional(),meetingAppId:Id.optional(),state:RedirectState.optional(),ttlSeconds:SessionTtlSeconds}));var CalendarAccess=named("CalendarAccess",z9.enum(["owner","writer","reader","freebusy"])),Calendar=named("Calendar",z9.object({id:Id,name:z9.string(),description:z9.string().nullable(),owner:Address.nullable(),isPrimary:z9.boolean(),access:CalendarAccess,canEdit:z9.boolean(),color:z9.string().nullable(),timeZone:z9.string().nullable(),isHidden:z9.boolean()})),CalendarList=List(Calendar),CalendarColor=z9.string().regex(/^#[0-9a-fA-F]{6}$/),CalendarIdInput=named("CalendarIdInput",AccountIdInput.extend({calendarId:Id})),CreateCalendar=named("CreateCalendar",z9.object({name:trimmed(MAX_NAME_LENGTH),description:trimmed(MAX_EVENT_BODY_LENGTH).nullable().optional(),color:CalendarColor.nullable().optional(),timeZone:trimmed(MAX_TIME_ZONE_LENGTH).nullable().optional()})),CreateCalendarInput=named("CreateCalendarInput",AccountIdInput.extend(CreateCalendar.shape)),UpdateCalendar=named("UpdateCalendar",z9.object({name:trimmed(MAX_NAME_LENGTH).optional(),description:trimmed(MAX_EVENT_BODY_LENGTH).nullable().optional(),color:CalendarColor.nullable().optional(),timeZone:trimmed(MAX_TIME_ZONE_LENGTH).nullable().optional()})),CalendarChangedField=named("CalendarChangedField",Calendar.keyof().exclude(["id"])),EventKind=named("EventKind",z9.enum(["single","series","occurrence","exception"])),AttendeeType=named("AttendeeType",z9.enum(["required","optional","resource"])),ResponseStatus=named("ResponseStatus",z9.enum(["none","organizer","accepted","tentative","declined","needs_action"])),EventStatus=named("EventStatus",z9.enum(["confirmed","tentative","cancelled"])),ShowAs=named("ShowAs",z9.enum(["free","busy","tentative","oof","working_elsewhere","unknown"])),Sensitivity=named("Sensitivity",z9.enum(["normal","personal","private","confidential"])),Attendee=named("Attendee",Address.extend({type:AttendeeType,response:ResponseStatus})),EventTime=named("EventTime",z9.object({dateTime:Timestamp,date:DateOnly.nullable(),timeZone:z9.string().nullable()})),CalendarEvent=named("CalendarEvent",z9.object({id:Id,calendarId:Id,kind:EventKind,seriesId:Id.nullable(),originalStart:Timestamp.nullable(),subject:z9.string().nullable(),snippet:z9.string().nullable(),location:z9.string().nullable(),start:EventTime,end:EventTime,isAllDay:z9.boolean(),organizer:Address.nullable(),attendees:z9.array(Attendee),isOrganizer:z9.boolean(),selfResponse:ResponseStatus,status:EventStatus,showAs:ShowAs,sensitivity:Sensitivity,onlineMeetingUrl:z9.string().nullable(),onlineMeetingProvider:z9.string().nullable(),iCalUid:z9.string().nullable(),recurrence:z9.array(z9.string()).nullable(),recurrenceTimeZone:z9.string().nullable(),cancelledStarts:z9.array(Timestamp).nullable(),categories:z9.array(z9.string()),hasAttachments:z9.boolean(),reminderMinutes:z9.number().int().nullable(),webLink:z9.string().nullable(),updatedAt:Timestamp})),CalendarEventPage=Page(CalendarEvent),CalendarEventChangedField=named("CalendarEventChangedField",CalendarEvent.keyof().exclude(["id","calendarId","seriesId","updatedAt"])),BodyContentType=named("BodyContentType",z9.enum(["text","html"])),EventBody=named("EventBody",z9.object({contentType:BodyContentType,content:z9.string()})),CalendarEventDetail=named("CalendarEventDetail",CalendarEvent.extend({body:EventBody.nullable()})),EventTimeInput=named("EventTimeInput",z9.union([z9.object({dateTime:z9.iso.datetime({local:!0}),timeZone:z9.string().min(1).max(MAX_TIME_ZONE_LENGTH)}),z9.object({date:DateOnly})])),AttendeeInput=named("AttendeeInput",AddressInput.extend({type:AttendeeType.default("required")})),RecurrenceLine=z9.string().max(MAX_RECURRENCE_LINE_LENGTH).regex(/^(RRULE|RDATE|EXDATE)[:;]/,"Expected an RRULE, RDATE or EXDATE line"),EventFields={subject:z9.string().max(MAX_EVENT_SUBJECT_LENGTH),body:EventBody.extend({content:z9.string().max(MAX_EVENT_BODY_LENGTH)}),location:z9.string().max(MAX_EVENT_LOCATION_LENGTH),start:EventTimeInput,end:EventTimeInput,attendees:z9.array(AttendeeInput).max(MAX_EVENT_ATTENDEES),recurrence:z9.array(RecurrenceLine).min(1).max(MAX_RECURRENCE_LINES),showAs:ShowAs.exclude(["unknown"]),sensitivity:Sensitivity,reminderMinutes:z9.number().int().min(0).max(MAX_REMINDER_MINUTES).nullable(),onlineMeetingUrl:z9.url({protocol:/^https$/}).max(MAX_URL_LENGTH)},Notify=z9.boolean().default(!0),CreateCalendarEvent=named("CreateCalendarEvent",z9.object({calendarId:Id,subject:EventFields.subject.optional(),body:EventFields.body.optional(),location:EventFields.location.optional(),start:EventFields.start,end:EventFields.end,attendees:EventFields.attendees.default([]),recurrence:EventFields.recurrence.optional(),showAs:EventFields.showAs.optional(),sensitivity:EventFields.sensitivity.optional(),reminderMinutes:EventFields.reminderMinutes.optional(),onlineMeeting:z9.boolean().default(!1),onlineMeetingUrl:EventFields.onlineMeetingUrl.optional(),onlineMeetingService:MeetingService.optional(),notify:Notify})),CreateCalendarEventInput=AccountIdInput.extend(CreateCalendarEvent.shape).superRefine((input,ctx)=>{if([input.onlineMeeting,input.onlineMeetingUrl!==void 0,input.onlineMeetingService!==void 0].filter(Boolean).length>1)ctx.addIssue({code:"custom",path:["onlineMeetingService"],message:"Give one of onlineMeeting, onlineMeetingUrl and onlineMeetingService",params:{reason:"multiple_meeting_options"}})}),UpdateCalendarEvent=named("UpdateCalendarEvent",z9.object({subject:EventFields.subject.optional(),body:EventFields.body.optional(),location:EventFields.location.optional(),start:EventFields.start.optional(),end:EventFields.end.optional(),attendees:EventFields.attendees.optional(),recurrence:EventFields.recurrence.optional(),showAs:EventFields.showAs.optional(),sensitivity:EventFields.sensitivity.optional(),reminderMinutes:EventFields.reminderMinutes.optional(),onlineMeetingUrl:EventFields.onlineMeetingUrl.nullable().optional(),notify:Notify})),DeleteCalendarEventQuery=named("DeleteCalendarEventQuery",z9.object({notify:QueryBoolean.default(!0),comment:z9.string().max(MAX_EVENT_COMMENT_LENGTH).optional()})),RsvpResponse=named("RsvpResponse",z9.enum(["accepted","tentative","declined"])),RespondToCalendarEventRequest=named("RespondToCalendarEventRequest",z9.object({response:RsvpResponse,comment:z9.string().max(MAX_EVENT_COMMENT_LENGTH).optional(),notify:z9.boolean().default(!0)})),CalendarEventIdInput=named("CalendarEventIdInput",AccountIdInput.extend({eventId:Id})),DeleteCalendarEventInput=named("DeleteCalendarEventInput",z9.object({params:CalendarEventIdInput,query:DeleteCalendarEventQuery})),ListCalendarEventsQuery=named("ListCalendarEventsQuery",z9.object({start:QueryTimestamp,end:QueryTimestamp,calendarId:Id.optional(),limit:pageLimit(MAX_CALENDAR_EVENT_PAGE_SIZE,DEFAULT_CALENDAR_EVENT_PAGE_SIZE),cursor:Cursor.optional()})),ListCalendarEventsInput=withDateRange(AccountIdInput.extend(ListCalendarEventsQuery.shape),{from:"start",to:"end",maxSpanMs:MAX_EVENT_RANGE_DAYS*DAY_MS}),BusyStatus=named("BusyStatus",z9.enum(["busy","tentative","oof","working_elsewhere","unknown"])),BusyBlock=named("BusyBlock",z9.object({start:Timestamp,end:Timestamp,status:BusyStatus})),AttendeeAvailability=named("AttendeeAvailability",z9.object({address:z9.string(),busy:z9.array(BusyBlock),error:z9.string().nullable()})),Weekday=named("Weekday",z9.enum(["mon","tue","wed","thu","fri","sat","sun"])),TimeOfDay=z9.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/,"Expected HH:MM"),WorkingHours=named("WorkingHours",z9.object({start:TimeOfDay,end:TimeOfDay,days:z9.array(Weekday).min(1).default(["mon","tue","wed","thu","fri"])}).refine((hours)=>hours.end>hours.start,{message:"workingHours end must be after start",path:["end"],params:{reason:"invalid_working_hours"}})),SlotMinutes=z9.number().int().min(MIN_SLOT_MINUTES).max(MAX_SLOT_MINUTES).default(30),AvailabilityOptions={start:QueryTimestamp,end:QueryTimestamp,durationMinutes:SlotMinutes,stepMinutes:SlotMinutes,timeZone:z9.string().min(1).max(MAX_TIME_ZONE_LENGTH).default("UTC"),workingHours:WorkingHours.optional(),maxSuggestions:z9.number().int().min(0).max(MAX_SUGGESTIONS).default(10),requireAll:z9.boolean().default(!1)},FindAvailabilityRequest=named("FindAvailabilityRequest",z9.object({attendees:z9.array(Email).min(1).max(MAX_AVAILABILITY_ATTENDEES),...AvailabilityOptions,includeAccount:z9.boolean().default(!0)})),FindAvailabilityInput=withDateRange(AccountIdInput.extend(FindAvailabilityRequest.shape),{from:"start",to:"end",maxSpanMs:MAX_AVAILABILITY_DAYS*DAY_MS}),TimeSlot=named("TimeSlot",z9.object({start:Timestamp,end:Timestamp})),WithheldReason=named("WithheldReason",z9.enum(["unresolved_attendees"])),Completeness={complete:z9.boolean(),unresolved:z9.array(z9.string()),withheldReason:WithheldReason.nullable()},Availability=named("Availability",z9.object({attendees:z9.array(AttendeeAvailability),suggestions:z9.array(TimeSlot),...Completeness})),AvailabilityMode=named("AvailabilityMode",z9.enum(["all","any"])),FindAccountsAvailabilityRequest=named("FindAccountsAvailabilityRequest",z9.object({accountIds:z9.array(Id).min(1).max(MAX_AVAILABILITY_ACCOUNTS).refine((ids)=>new Set(ids).size===ids.length,"accountIds lists an account twice"),mode:AvailabilityMode.default("all"),attendees:z9.array(Email).max(MAX_AVAILABILITY_ATTENDEES).default([]),...AvailabilityOptions})),FindAccountsAvailabilityInput=withDateRange(FindAccountsAvailabilityRequest,{from:"start",to:"end",maxSpanMs:MAX_AVAILABILITY_DAYS*DAY_MS}),AccountAvailability=named("AccountAvailability",AttendeeAvailability.extend({accountId:Id})),AccountsTimeSlot=named("AccountsTimeSlot",TimeSlot.extend({accountIds:z9.array(Id)})),AccountsAvailability=named("AccountsAvailability",z9.object({accounts:z9.array(AccountAvailability),attendees:z9.array(AttendeeAvailability),suggestions:z9.array(AccountsTimeSlot),...Completeness}));import*as z11 from"zod";import*as z10 from"zod";var customDomainHostname=(example)=>z10.string().trim().toLowerCase().regex(/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){2,}[a-z](?:[a-z0-9-]{0,61}[a-z0-9])$/,`Expected a subdomain such as "${example}"`),CUSTOM_DOMAIN_STATUSES=["pending","active","failed"],customDomainRecord=()=>z10.object({type:z10.string().meta({example:"CNAME"}),name:z10.string(),value:z10.string()});var ConnectDomainHostname=named("ConnectDomainHostname",customDomainHostname("connect.example.com")),ConnectDomainStatus=named("ConnectDomainStatus",z11.enum(CUSTOM_DOMAIN_STATUSES)),ConnectDomainRecord=named("ConnectDomainRecord",customDomainRecord()),ConnectDomain=named("ConnectDomain",z11.object({hostname:ConnectDomainHostname,status:ConnectDomainStatus,records:z11.array(ConnectDomainRecord),ownership:ConnectDomainRecord,error:z11.string().nullable(),activatedAt:Timestamp.nullable(),createdAt:Timestamp})),SetConnectDomain=named("SetConnectDomain",z11.object({hostname:ConnectDomainHostname}));import*as z12 from"zod";var ContactEmailType=named("ContactEmailType",z12.enum(["home","work","other"])),ContactPhoneType=named("ContactPhoneType",z12.enum(["mobile","home","work","other"])),ContactEmail=named("ContactEmail",Address.pick({address:!0}).extend({type:ContactEmailType.nullable()})),ContactPhone=named("ContactPhone",z12.object({number:z12.string(),type:ContactPhoneType.nullable()})),ContactFolder=named("ContactFolder",z12.object({id:Id,name:z12.string(),isDefault:z12.boolean()})),ContactFolderList=List(ContactFolder),Birthday=z12.string().regex(/^(\d{4}-|--)\d{2}-\d{2}$/,"Expected YYYY-MM-DD or --MM-DD"),Contact=named("Contact",z12.object({id:Id,folderId:Id.nullable(),displayName:z12.string().nullable(),givenName:z12.string().nullable(),middleName:z12.string().nullable(),familyName:z12.string().nullable(),nickname:z12.string().nullable(),emails:z12.array(ContactEmail),phones:z12.array(ContactPhone),organization:z12.string().nullable(),jobTitle:z12.string().nullable(),department:z12.string().nullable(),birthday:z12.string().nullable(),notes:z12.string().nullable(),hasPhoto:z12.boolean().nullable(),updatedAt:Timestamp})),ContactPage=Page(Contact),ContactChangedField=named("ContactChangedField",z12.enum([...Contact.keyof().exclude(["id","updatedAt"]).options,"photo"])),ContactEmailInput=named("ContactEmailInput",AddressInput.pick({address:!0}).extend({type:ContactEmailType.optional()})),ContactPhoneInput=named("ContactPhoneInput",z12.object({number:trimmed(MAX_CONTACT_PHONE_LENGTH),type:ContactPhoneType.optional()})),ContactFields={displayName:trimmed(MAX_CONTACT_FIELD_LENGTH),givenName:trimmed(MAX_CONTACT_FIELD_LENGTH),middleName:trimmed(MAX_CONTACT_FIELD_LENGTH),familyName:trimmed(MAX_CONTACT_FIELD_LENGTH),nickname:trimmed(MAX_CONTACT_FIELD_LENGTH),organization:trimmed(MAX_CONTACT_FIELD_LENGTH),jobTitle:trimmed(MAX_CONTACT_FIELD_LENGTH),department:trimmed(MAX_CONTACT_FIELD_LENGTH),birthday:Birthday,notes:z12.string().max(MAX_CONTACT_NOTES_LENGTH)},CreateContact=named("CreateContact",z12.object({folderId:Id.optional(),displayName:ContactFields.displayName.optional(),givenName:ContactFields.givenName.optional(),middleName:ContactFields.middleName.optional(),familyName:ContactFields.familyName.optional(),nickname:ContactFields.nickname.optional(),emails:z12.array(ContactEmailInput).max(MAX_CONTACT_ENTRIES).optional(),phones:z12.array(ContactPhoneInput).max(MAX_CONTACT_ENTRIES).optional(),organization:ContactFields.organization.optional(),jobTitle:ContactFields.jobTitle.optional(),department:ContactFields.department.optional(),birthday:ContactFields.birthday.optional(),notes:ContactFields.notes.optional()})),CreateContactInput=AccountIdInput.extend(CreateContact.shape).refine((c)=>c.displayName!==void 0||c.givenName!==void 0||c.familyName!==void 0||c.organization!==void 0||(c.emails?.length??0)>0||(c.phones?.length??0)>0,{message:"A contact needs a name, an organization, an email address or a phone number",params:{reason:"contact_unidentifiable"}}),UpdateContact=named("UpdateContact",z12.object({displayName:ContactFields.displayName.nullable().optional(),givenName:ContactFields.givenName.nullable().optional(),middleName:ContactFields.middleName.nullable().optional(),familyName:ContactFields.familyName.nullable().optional(),nickname:ContactFields.nickname.nullable().optional(),emails:z12.array(ContactEmailInput).max(MAX_CONTACT_ENTRIES).optional(),phones:z12.array(ContactPhoneInput).max(MAX_CONTACT_ENTRIES).optional(),organization:ContactFields.organization.nullable().optional(),jobTitle:ContactFields.jobTitle.nullable().optional(),department:ContactFields.department.nullable().optional(),birthday:ContactFields.birthday.nullable().optional(),notes:ContactFields.notes.nullable().optional()})),ContactIdInput=named("ContactIdInput",AccountIdInput.extend({contactId:Id})),ListContactsQuery=named("ListContactsQuery",z12.object({q:trimmed(MAX_FILTER_QUERY_LENGTH).optional(),folderId:Id.optional(),limit:pageLimit(MAX_CONTACT_PAGE_SIZE,DEFAULT_CONTACT_PAGE_SIZE),cursor:Cursor.optional()}));import*as z13 from"zod";var Reachability=named("Reachability",z13.enum(["safe","risky","invalid","unknown"])),CreateEmailValidation=named("CreateEmailValidation",z13.object({addresses:z13.array(Email).min(1).max(MAX_EMAIL_VALIDATIONS),suppressInvalid:z13.boolean().default(!1),refresh:z13.boolean().default(!1)})),EmailValidationResult=named("EmailValidationResult",z13.object({address:z13.string(),reachability:Reachability,validSyntax:z13.boolean(),domainAcceptsMail:z13.boolean().nullable(),deliverable:z13.boolean().nullable(),disabled:z13.boolean().nullable(),fullInbox:z13.boolean().nullable(),catchAll:z13.boolean().nullable(),disposable:z13.boolean().nullable(),roleAccount:z13.boolean().nullable(),suppressed:z13.boolean(),cached:z13.boolean(),checkedAt:Timestamp})),EmailValidation=named("EmailValidation",z13.object({results:z13.array(EmailValidationResult)})),EmailValidationRecord=named("EmailValidationRecord",z13.object({id:Id,createdAt:Timestamp,...EmailValidationResult.shape})),EmailValidationRecordPage=Page(EmailValidationRecord),ListEmailValidationsQuery=named("ListEmailValidationsQuery",z13.object({address:z13.string().trim().max(MAX_EMAIL_LENGTH).optional(),reachability:Reachability.optional(),...Paged}));import{oc}from"@orpc/contract";import*as z17 from"zod";import*as z15 from"zod";import*as z14 from"zod";var PlanCreditPack=named("PlanCreditPack",z14.object({credits:z14.number().int().min(1).max(MAX_CREDIT_PACK_CREDITS),amount:z14.number().int().min(0)})),PlanCreditPacks=z14.array(PlanCreditPack).max(MAX_CREDIT_PACKS_PER_PLAN).superRefine((packs,ctx)=>{for(let{index,field,message}of creditPackProblems(packs))ctx.addIssue({code:"custom",path:[index,field],message})});function creditPackProblems(packs){let problems=[];return packs.forEach((pack,index)=>{let before=packs[index-1];if(!before)return;if(pack.credits<=before.credits)problems.push({index,field:"credits",message:"Credits must be more than the pack before"});else if(BigInt(pack.amount)*BigInt(before.credits)>BigInt(before.amount)*BigInt(pack.credits))problems.push({index,field:"amount",message:"The price per credit must not be more than the pack before's"})}),problems}var PlanModules=named("PlanModules",z15.object({mail:z15.boolean(),calendar:z15.boolean(),contacts:z15.boolean(),tracking:z15.boolean(),ai:z15.boolean(),meetings:z15.boolean(),sharedMailboxes:z15.boolean()}));var BILLING_COMPONENTS=["mail","calendarContacts","ai","sharedMailboxes","meetings"],COMPONENT_MODULES={mail:["mail"],calendarContacts:["calendar","contacts"],ai:["ai"],sharedMailboxes:["sharedMailboxes"],meetings:["meetings"]},MODULE_TOGGLES=[...BILLING_COMPONENTS,"tracking"];var TOGGLE_MODULES={...COMPONENT_MODULES,tracking:["tracking"]};var METERS=["mailAccounts","calendarContactsAccounts","meetingAccounts","sends","aiClassifications","aiDrafts","storage"],Meter=named("Meter",z15.enum(METERS)),PlanIncluded=named("PlanIncluded",z15.object({mailAccounts:z15.number().int().min(0).nullable(),calendarContactsAccounts:z15.number().int().min(0).nullable(),meetingAccounts:z15.number().int().min(0).nullable(),sendsPerMonth:z15.number().int().min(0).nullable(),aiClassificationsPerMonth:z15.number().int().min(0).nullable(),aiDraftsPerMonth:z15.number().int().min(0).nullable(),storageBytes:z15.number().int().min(0).nullable(),validationCredits:z15.number().int().min(0).max(MAX_VALIDATION_CREDITS).nullable()})),Amount=z15.number().int().min(0),PlanPricing=named("PlanPricing",z15.object({currency:z15.string().regex(/^[a-z]{3}$/),baseMonthly:Amount,baseAnnual:Amount.nullable(),components:z15.object({mail:Amount,calendarContacts:Amount,ai:Amount,sharedMailboxes:Amount,meetings:Amount}),sendsPer1k:Amount,aiClassificationsPer1k:Amount,aiDraftsPer1k:Amount,storagePerGbMonth:Amount,creditPacks:PlanCreditPacks})),MIN_AUDIT_LOG_RETENTION_DAYS=1,MAX_AUDIT_LOG_RETENTION_DAYS=3650,PlanFeatures=named("PlanFeatures",z15.object({sso:z15.boolean(),scim:z15.boolean(),auditLogRetentionDays:z15.number().int().min(MIN_AUDIT_LOG_RETENTION_DAYS).max(MAX_AUDIT_LOG_RETENTION_DAYS).nullable(),sla:z15.boolean(),customDpa:z15.boolean(),prioritySupport:z15.boolean()})),GATED_PLAN_FEATURES=["sso","scim"],PlanLimits=named("PlanLimits",z15.object({maxAccounts:z15.number().int().min(0).nullable(),sendsPerDay:z15.number().int().min(0).nullable(),emailValidationsPerDay:z15.number().int().min(0).nullable(),apiCallsPerMinute:z15.number().int().min(1).nullable(),apiCallsPerMinutePerKey:z15.number().int().min(1).nullable(),webhookEndpoints:z15.number().int().min(0).nullable(),bodyStorageAllowed:z15.boolean(),storageBytes:z15.number().int().min(0).nullable(),included:PlanIncluded,pricing:PlanPricing.nullable(),features:PlanFeatures}));import*as z16 from"zod";var TemplateSource=named("TemplateSource",z16.enum(["handlebars","react-email"])),TemplateFormat=named("TemplateFormat",z16.enum(["html","markdown"])),TemplateField=named("TemplateField",z16.enum(["subject","html","text","previewText"])),TemplateFields=named("TemplateFields",z16.object({subject:z16.string().max(MAX_TEMPLATE_SUBJECT_LENGTH).nullable(),html:z16.string().max(MAX_TEMPLATE_BODY_LENGTH).nullable(),text:z16.string().max(MAX_TEMPLATE_BODY_LENGTH).nullable(),previewText:z16.string().max(MAX_TEMPLATE_PREVIEW_TEXT_LENGTH).nullable(),format:TemplateFormat})),TemplateSummary=named("TemplateSummary",z16.object({id:Id,accountId:Id.nullable(),name:z16.string(),description:z16.string().nullable(),source:TemplateSource,externalRef:z16.string().nullable(),createdAt:Timestamp,updatedAt:Timestamp})),Template=named("Template",TemplateSummary.extend(TemplateFields.shape)),TemplateSummaryPage=Page(TemplateSummary),RenderParams=named("RenderParams",z16.record(z16.string(),z16.unknown())),RenderedTemplate=named("RenderedTemplate",z16.object({subject:z16.string().nullable(),html:z16.string().nullable(),text:z16.string().nullable(),previewText:z16.string().nullable()})),TemplateIdInput=named("TemplateIdInput",z16.object({templateId:Id})),TemplateName=trimmed(MAX_TEMPLATE_NAME_LENGTH),TemplateDescription=z16.string().max(MAX_TEMPLATE_DESCRIPTION_LENGTH),ListTemplatesQuery=named("ListTemplatesQuery",z16.object({...Paged,accountId:Id.optional(),usableBy:Id.optional(),name:z16.string().max(MAX_TEMPLATE_NAME_LENGTH).optional(),query:z16.string().trim().max(MAX_TEMPLATE_NAME_LENGTH).optional()})),CreateTemplate=named("CreateTemplate",z16.object({accountId:Id.optional(),name:TemplateName,description:TemplateDescription.optional(),subject:TemplateFields.shape.subject.unwrap().optional(),html:TemplateFields.shape.html.unwrap().optional(),text:TemplateFields.shape.text.unwrap().optional(),previewText:TemplateFields.shape.previewText.unwrap().optional(),format:TemplateFormat.default("html"),source:TemplateSource.default("handlebars"),externalRef:ExternalRef.optional()})),UpdateTemplate=named("UpdateTemplate",z16.object({name:TemplateName.optional(),description:TemplateDescription.nullable().optional(),subject:TemplateFields.shape.subject.optional(),html:TemplateFields.shape.html.optional(),text:TemplateFields.shape.text.optional(),previewText:TemplateFields.shape.previewText.optional(),format:TemplateFormat.optional(),source:TemplateSource.optional(),externalRef:ExternalRef.nullable().optional()})),RenderTemplateRequest=named("RenderTemplateRequest",z16.object({templateId:Id.optional(),accountId:Id.optional(),subject:z16.string().max(MAX_TEMPLATE_SUBJECT_LENGTH).optional(),html:z16.string().max(MAX_TEMPLATE_BODY_LENGTH).optional(),text:z16.string().max(MAX_TEMPLATE_BODY_LENGTH).optional(),previewText:z16.string().max(MAX_TEMPLATE_PREVIEW_TEXT_LENGTH).optional(),format:TemplateFormat.optional(),params:RenderParams.default({})}).superRefine((request,ctx)=>{let{templateId,subject,html,text,previewText,format}=request;if(templateId===void 0)return;if([subject,html,text,previewText,format].some((field)=>field!==void 0))ctx.addIssue({code:"custom",path:["templateId"],message:"Render either a template or inline fields, not both",params:{reason:"template_and_inline_fields"}})}));var InputIssue=named("InputIssue",z17.object({path:z17.array(z17.union([z17.string(),z17.number()])),message:z17.string()})),InvalidInputData=named("InvalidInputData",z17.object({issues:z17.array(InputIssue)})),UnknownIdsData=named("UnknownIdsData",z17.object({ids:z17.array(Id)})),SuppressedData=named("SuppressedData",z17.object({addresses:z17.array(z17.string())})),SyncKindsData=named("SyncKindsData",z17.object({kinds:z17.array(SyncKind)})),PlanFeatureData=named("PlanFeatureData",z17.object({feature:z17.enum(GATED_PLAN_FEATURES)})),TemplateFieldData=named("TemplateFieldData",z17.object({field:TemplateField})),SsoRequiredData=named("SsoRequiredData",z17.object({providerId:z17.string(),organization:z17.string()})),PLAN_LIMIT_KINDS=["mail_accounts","calendar_contacts_accounts","meeting_accounts","accounts","sends_per_day","sends_per_month","webhook_endpoints","validations_per_day","storage_bytes","body_storage","ai_classifications","ai_drafts","validation_credits","spend_cap","plan"],PlanLimitKind=named("PlanLimitKind",z17.enum(PLAN_LIMIT_KINDS)),PlanLimitData=named("PlanLimitData",z17.object({limit:PlanLimitKind,value:z17.number().nullable(),usage:z17.number().nullable(),upgradeUrl:z17.string()})),ValidationCreditsData=named("ValidationCreditsData",PlanLimitData.extend({balance:z17.number().int(),needed:z17.number().int()})),LIMIT_DATA_REASONS=["plan_limit_accounts","plan_limit_sends","plan_limit_validations","plan_limit_ai","plan_limit_webhooks","plan_limit_storage","plan_required","validation_credits_exhausted","spend_cap_reached"],LIMIT_HIT_REASONS=[...LIMIT_DATA_REASONS,"plan_feature_unavailable"],LimitHitReason=named("LimitHitReason",z17.enum(LIMIT_HIT_REASONS)),REASONS={invalid_input:{status:400,data:InvalidInputData},invalid_cursor:{status:400},invalid_range:{status:400},payload_too_large:{status:413},invalid_payload:{status:400},invalid_signature:{status:400},route_not_found:{status:404},internal_error:{status:500},invalid_idempotency_key:{status:400},idempotency_key_reused:{status:422},idempotency_key_in_progress:{status:409},rate_limited:{status:429},too_many_requests:{status:429},overloaded:{status:503},unauthenticated:{status:401},api_key_expired:{status:401},missing_scope:{status:403},role_forbidden:{status:403},account_token_forbidden:{status:403},account_scope_forbidden:{status:403},api_key_required:{status:403},mcp_only_key:{status:403},session_required:{status:403},owner_required:{status:403},platform_admin_required:{status:403},email_verification_required:{status:403},console_only:{status:403},sso_required:{status:403,data:SsoRequiredData},two_factor_required:{status:403},session_not_fresh:{status:403},cross_site_request:{status:403},plan_limit_accounts:{status:403,data:PlanLimitData},plan_limit_sends:{status:403,data:PlanLimitData},plan_limit_validations:{status:403,data:PlanLimitData},plan_limit_ai:{status:403,data:PlanLimitData},plan_limit_webhooks:{status:403,data:PlanLimitData},plan_limit_storage:{status:403,data:PlanLimitData},plan_required:{status:402,data:PlanLimitData},spend_cap_reached:{status:402,data:PlanLimitData},plan_module_unavailable:{status:403},plan_feature_unavailable:{status:403,data:PlanFeatureData},storage_unavailable:{status:400},connect_grants_nothing:{status:400},tenant_not_found:{status:404},billing_past_due:{status:402},billing_disabled:{status:404},billing_unavailable:{status:502},billing_tax_failed:{status:502},billing_subscription_exists:{status:409},billing_customer_missing:{status:409},plan_not_billable:{status:409},plan_not_offered:{status:409},billing_subscription_missing:{status:409},billing_not_active:{status:409},billing_cancel_pending:{status:409},plan_unchanged:{status:409},plan_currency_mismatch:{status:409},plan_interval_unavailable:{status:409},plan_interval_change:{status:409},billing_payment_failed:{status:402},plan_change_quote_changed:{status:409},billing_checkout_in_progress:{status:409},validation_credits_exhausted:{status:402,data:ValidationCreditsData},account_not_found:{status:404},unknown_account:{status:422,data:UnknownIdsData},account_paused:{status:409},account_disabled:{status:409},account_reauth_required:{status:409},account_misconfigured:{status:409},mail_disabled:{status:409},calendar_disabled:{status:409},contacts_disabled:{status:409},sync_kinds_disabled:{status:409,data:SyncKindsData},mcp_key_not_found:{status:404},mcp_key_limit:{status:409},provider_not_found:{status:404},provider_throttled:{status:429},provider_unavailable:{status:502},provider_forbidden:{status:403},provider_conflict:{status:409},provider_unsupported:{status:400},provider_rejected:{status:400},outcome_unknown:{status:502},message_not_found:{status:404},unknown_mailbox:{status:422,data:UnknownIdsData},mailbox_not_label:{status:400},mailbox_protected:{status:409},mailbox_parent:{status:400},thread_not_found:{status:404},identity_not_found:{status:404},identities_managed_by_provider:{status:400},identity_exists:{status:409},identity_not_custom:{status:400},draft_not_found:{status:404},draft_lost:{status:502},send_not_found:{status:404},mail_merge_not_found:{status:404},attachments_too_large:{status:400},not_a_draft:{status:400},unknown_reply_target:{status:422},from_not_allowed:{status:400},tracking_unsupported:{status:400},honeypot_needs_click_tracking:{status:400},list_send_recipients:{status:400},suppressed:{status:400,data:SuppressedData},no_recipients:{status:400},list_unsubscribe_unavailable:{status:400},send_not_reschedulable:{status:409},send_not_cancellable:{status:409},duplicate_message_id:{status:409},suppression_list_not_found:{status:404},suppression_entry_not_found:{status:404},suppression_review_not_found:{status:404},suppression_list_exists:{status:409},suppression_list_not_empty:{status:409},tracking_domain_unavailable:{status:400},tracking_domain_not_found:{status:404},tracking_domain_not_allowed:{status:400},tracking_domain_taken:{status:409},tracking_domain_edge_unavailable:{status:502},connect_domain_unavailable:{status:400},connect_domain_not_found:{status:404},connect_domain_not_allowed:{status:400},connect_domain_taken:{status:409},connect_domain_in_use:{status:409},connect_domain_edge_unavailable:{status:502},connect_domain_inactive:{status:409},connect_domain_unsupported:{status:400},template_not_found:{status:404},unknown_template:{status:422},template_and_inline_fields:{status:400},invalid_template:{status:400,data:TemplateFieldData},template_account_mismatch:{status:400},template_render_failed:{status:400,data:TemplateFieldData},params_too_large:{status:400},template_name_taken:{status:409},calendar_not_found:{status:404},calendar_event_not_found:{status:404},contact_not_found:{status:404},photo_not_found:{status:404},unknown_calendar:{status:422},unknown_contact_folder:{status:422},calendar_read_only:{status:403},calendar_primary:{status:409},organizer_cannot_respond:{status:409},invalid_time_zone:{status:400},invalid_working_hours:{status:400},scheduling_link_not_found:{status:404},booking_not_found:{status:404},slot_unavailable:{status:409},slot_taken:{status:409},booking_cancelled:{status:409},mixed_event_times:{status:400},invalid_recurrence:{status:400},recurrence_on_instance:{status:400},contact_unidentifiable:{status:400},multiple_meeting_options:{status:400},meeting_service_not_connected:{status:409},meeting_service_reauth_required:{status:409},meeting_host_missing:{status:409},meeting_recurrence_unsupported:{status:400},meeting_service_rejected:{status:400},meeting_deleted:{status:409},meeting_service_unavailable:{status:502},meeting_app_not_found:{status:404},unknown_meeting_app:{status:422},meeting_connection_not_found:{status:404},meeting_connect_session_not_found:{status:404},meeting_app_exists:{status:409},meeting_app_in_use:{status:409},meeting_app_mismatch:{status:400},meeting_app_secret_unreadable:{status:400},not_testable:{status:400},autodiscovery_failed:{status:422},password_required:{status:400},oauth_servers_fixed:{status:400},dav_discovery_failed:{status:400},dav_unsupported:{status:400},login_failed:{status:400},server_not_allowed:{status:400},plaintext_not_allowed:{status:400},not_dav_server:{status:400},not_imap_account:{status:400},password_not_stored:{status:400},password_unsupported:{status:400},connect_session_not_found:{status:404},session_used:{status:409},session_expired:{status:400},provider_not_allowed:{status:400},redirect_uri_not_allowed:{status:400},access_denied:{status:400},access_token_rejected:{status:400},account_exists:{status:409},account_mismatch:{status:409},consent_failed:{status:400},consent_revoked:{status:400},consent_unverified:{status:400},credential_conflict:{status:400},credential_missing:{status:400},identity_failed:{status:400},identity_unavailable:{status:400},invalid_client:{status:400},interaction_required:{status:400},invalid_grant:{status:400},missing_refresh_token:{status:400},unknown_oauth_app:{status:422},oauth_app_not_found:{status:404},oauth_app_mismatch:{status:400},oauth_app_in_use:{status:409},oauth_app_secret_unreadable:{status:400},google_imap_requires_own_app:{status:400},org_connection_account:{status:400},shared_mailbox_account:{status:400},gmail_push_unsupported:{status:400},push_unavailable:{status:400},cloud_required:{status:400},provider_error:{status:400},provider_not_configured:{status:400},unsupported_provider:{status:400},scope_denied:{status:400},token_exchange_failed:{status:400},org_connection_not_found:{status:404},org_connection_secret_unreadable:{status:400},consent_required:{status:409},delegate_missing:{status:400},directory_unavailable:{status:409},directory_already_connected:{status:409},directory_mismatch:{status:400},directory_id_not_allowed:{status:400},credential_mismatch:{status:400},connection_in_use:{status:409},invalid_certificate:{status:400},invalid_service_account_key:{status:400},not_microsoft:{status:400},user_required:{status:400},shared_mailbox_unsupported:{status:400},scope_missing:{status:400},shared_mailbox_inaccessible:{status:422},group_unsupported:{status:400},group_relay_inaccessible:{status:422},group_alias_unverified:{status:409},group_alias_missing:{status:409},webhook_endpoint_not_found:{status:404},endpoint_disabled:{status:409},event_not_found:{status:404},replay_range_too_large:{status:400},mailbox_outside_accounts:{status:400},https_required:{status:400},export_not_found:{status:404},export_not_ready:{status:409},export_failed:{status:409},export_running:{status:409},export_in_progress:{status:409},export_expired:{status:410},export_corrupt:{status:500},confirmation_mismatch:{status:400},erase_scheduled:{status:409},erase_started:{status:409},erase_not_found:{status:404},ai_disabled:{status:400},ai_unavailable:{status:502},ai_suspended:{status:502},ai_refused:{status:400},unknown_message:{status:422},email_validation_disabled:{status:400},email_validation_unavailable:{status:502},email_validation_suspended:{status:502}};function reasonGuards(table){let isReason=(code)=>typeof code==="string"&&Object.hasOwn(table,code);return{isReason,reasonOf(error){if(typeof error!=="object"||error===null||!("code"in error))return null;return isReason(error.code)?error.code:null}}}var v1Guards=reasonGuards(REASONS),{isReason:isErrorReason,reasonOf}=v1Guards;function errorMap(table,conditions){let map={};for(let[reason,message]of Object.entries(conditions)){let definition=table[reason];map[reason]={status:definition.status,message,...definition.data!==void 0&&{data:definition.data}}}return map}function errors(conditions){return errorMap(REASONS,conditions)}var GLOBAL_ERRORS=errors({invalid_input:"The request does not match the operation's input schema (a malformed body included); data.issues names each problem.",unauthenticated:"No usable credential: none was sent, or it is malformed, unknown, revoked or an expired account token.",api_key_expired:"The API key is past its expiry (or the grace of the rotation that replaced it).",missing_scope:"The API key lacks the scope the operation needs.",role_forbidden:"The member's role does not allow the operation.",account_token_forbidden:"An account token called an operation account tokens cannot call.",account_scope_forbidden:"A credential limited to specific accounts called an operation that reaches beyond them.",mcp_only_key:"An MCP-only key (minted for an end user's MCP client) was used outside the MCP server.",rate_limited:"The tenant used up its plan's request rate; Retry-After says when the window ends.",too_many_requests:"The credential has too many requests in flight at once; retry after Retry-After.",overloaded:"The API is shedding load; retry after Retry-After.",payload_too_large:"The request body is larger than the API accepts.",internal_error:"An unexpected failure on our side.",invalid_idempotency_key:"POST only: the Idempotency-Key header is not 1 to 255 printable ASCII characters.",idempotency_key_reused:"POST only: the Idempotency-Key was used in the last 24 hours with another method, path or body.",idempotency_key_in_progress:"POST only: the request first sent with this Idempotency-Key has not been answered yet."}),base=oc.errors(GLOBAL_ERRORS),BOARD_SESSION_ONLY="The caller is not a console session (only console sessions may call this).",SESSION_ONLY=errors({session_required:BOARD_SESSION_ONLY}),OWNER_ONLY=errors({session_required:BOARD_SESSION_ONLY,owner_required:"The caller does not own the organization."}),PLATFORM_ONLY=errors({session_required:BOARD_SESSION_ONLY,platform_admin_required:"The caller is not a platform administrator.",email_verification_required:"The platform administrator has not verified their email address yet."}),ACCOUNT_NOT_FOUND=errors({account_not_found:"The account does not exist, or the caller is an account token for another account."}),MAIL_ACCOUNT={...planModule("mail"),...ACCOUNT_NOT_FOUND,...errors({mail_disabled:"The account's mail is off (settings.mailEnabled)."})};function accountState(account="The account"){return errors({account_paused:`${account} is paused.`,account_disabled:`${account} is disabled.`,account_reauth_required:`${account} must be reconnected: its stored grant or login no longer works.`,account_misconfigured:`${account}'s stored grant cannot be used until an administrator fixes what it relies on (the tenant's OAuth app or organization connection is refused, missing or unreadable).`})}var PROVIDER_FAILURES=errors({provider_throttled:"The provider asked us to slow down (with Retry-After when it named a wait).",provider_unavailable:"The provider could not be reached (with Retry-After when it named a wait)."});function bodyStorage(field){return errors({storage_unavailable:`${field} is cache or full and no object store is configured.`,plan_limit_storage:`${field} is cache or full and the tenant's plan does not include body storage.`})}function planModule(module){return errors({plan_module_unavailable:`The tenant's plan does not include ${module}.`})}var PLAN_REQUIRED_CONDITION="The workspace has no plan and is not its owner's Free workspace (the Free plan covers one workspace per owner): choose a plan under Billing.",PLAN_REQUIRED=errors({plan_required:PLAN_REQUIRED_CONDITION});function spendCapCondition(what){return`The workspace's spend cap is reached and its action is pause: refused when ${what}, which would be billed. Raising or clearing the cap lifts it.`}var PLAN_LIMIT_ACCOUNTS=errors({plan_limit_accounts:"The tenant already has as many accounts as its plan allows.",plan_required:PLAN_REQUIRED_CONDITION,spend_cap_reached:spendCapCondition("the change would take an account count past what the plan includes")}),BILLING_PAST_DUE=errors({billing_past_due:"The tenant's payment is past due beyond the grace period (sends already queued or scheduled still go out)."}),INVALID_CURSOR=errors({invalid_cursor:"The cursor is not one this list issued."});import*as z21 from"zod";import*as z18 from"zod";var MailboxRole=named("MailboxRole",z18.enum(["inbox","sent","drafts","trash","junk","archive","all","important","custom"])),MailboxKind=named("MailboxKind",z18.enum(["folder","label"])),Mailbox=named("Mailbox",z18.object({id:Id,name:z18.string(),path:z18.string(),parentId:Id.nullable(),role:MailboxRole,kind:MailboxKind,totalCount:z18.number().int().nullable(),unreadCount:z18.number().int().nullable()})),MailboxList=List(Mailbox),MailboxIdInput=named("MailboxIdInput",AccountIdInput.extend({mailboxId:Id})),CreateMailbox=named("CreateMailbox",z18.object({name:trimmed(MAX_DISPLAY_NAME_LENGTH),parentId:Id.optional()})),CreateMailboxInput=named("CreateMailboxInput",AccountIdInput.extend(CreateMailbox.shape)),UpdateMailbox=named("UpdateMailbox",z18.object({name:trimmed(MAX_DISPLAY_NAME_LENGTH).optional(),parentId:Id.nullable().optional()})),MailboxChangedField=named("MailboxChangedField",z18.enum(["name","path","parentId","role","kind"])),MessageDirection=named("MessageDirection",z18.enum(["inbound","outbound"])),MessagePriority=named("MessagePriority",z18.enum(["high","normal","low"])),MessageSummary=named("MessageSummary",z18.object({id:Id,threadId:ProviderId.nullable(),internetMessageId:z18.string().nullable(),mailboxIds:z18.array(Id),subject:z18.string().nullable(),from:Address.nullable(),to:z18.array(Address),cc:z18.array(Address),snippet:z18.string().nullable(),receivedAt:Timestamp.nullable(),sentAt:Timestamp.nullable(),isRead:z18.boolean(),isFlagged:z18.boolean(),isDraft:z18.boolean(),hasAttachments:z18.boolean(),categories:z18.array(z18.string()),direction:MessageDirection,outboxId:Id.nullable(),reference:z18.string().nullable(),importance:MessagePriority.nullable()})),MessageChangedField=named("MessageChangedField",z18.enum(["threadId","internetMessageId","subject","from","to","cc","bcc","replyTo","snippet","receivedAt","sentAt","isRead","isFlagged","isDraft","hasAttachments","categories","mailboxIds","direction","outboxId","importance"])),MessageSummaryPage=Page(MessageSummary),Attachment=named("Attachment",z18.object({id:ProviderId,filename:z18.string().nullable(),contentType:z18.string(),size:z18.number().int().nullable(),isInline:z18.boolean(),contentId:z18.string().nullable()})),ReplyKind=named("ReplyKind",z18.enum(["opt_out","auto_reply","bounce","human_reply"])),AutoReplyCategory=named("AutoReplyCategory",z18.enum(["out_of_office","left_company","address_changed","ticket_acknowledgment","challenge_response","other"])),MessageClassification=named("MessageClassification",z18.object({kind:ReplyKind,probabilities:z18.record(z18.string(),z18.number()),optOutProbability:z18.number(),autoReplyCategory:AutoReplyCategory.nullable(),returnDate:DateOnly.nullable(),alternateContacts:z18.array(z18.string()),outboxId:Id.nullable(),model:z18.string(),classifiedAt:Timestamp})),MessageBody=named("MessageBody",z18.object({text:z18.string().nullable(),html:z18.string().nullable()})),MessageHeader=named("MessageHeader",z18.object({name:z18.string(),value:z18.string()})),Message=named("Message",MessageSummary.extend({bcc:z18.array(Address),replyTo:z18.array(Address),body:MessageBody,attachments:z18.array(Attachment),classification:MessageClassification.nullable(),inReplyTo:z18.string().nullable(),references:z18.array(z18.string()),headers:z18.array(MessageHeader).optional()})),MessagePage=Page(Message),SearchSource=named("SearchSource",z18.enum(["auto","index","provider"])),SearchResultSource=named("SearchResultSource",SearchSource.exclude(["auto"])),SearchHit=named("SearchHit",MessageSummary.extend({id:Id.nullable()})),SearchPage=named("SearchPage",z18.object({...pageFields(SearchHit),source:SearchResultSource})),BodyStorageUsage=named("BodyStorageUsage",z18.object({available:z18.boolean(),storedMessages:z18.number().int(),storedBytes:z18.number().int(),cachedBytes:z18.number().int(),cacheBudgetBytes:z18.number().int(),cacheMaxAgeDays:z18.number().int()})),MessageIdInput=named("MessageIdInput",AccountIdInput.extend({messageId:Id})),AttachmentIdInput=named("AttachmentIdInput",MessageIdInput.extend({attachmentId:ProviderId})),MessageDetailInclude=named("MessageDetailInclude",z18.enum(["headers"])),MessageListInclude=named("MessageListInclude",z18.enum(["body","headers"])),includeList=(item)=>z18.union([z18.string(),z18.array(item)]).transform((v)=>Array.isArray(v)?v:v.split(",").map((s)=>s.trim())).pipe(z18.array(item)),GetMessageInput=named("GetMessageInput",MessageIdInput.extend({include:includeList(MessageDetailInclude).optional()})),MessageFilters={from:trimmed(MAX_EMAIL_LENGTH).optional(),to:trimmed(MAX_EMAIL_LENGTH).optional(),unread:QueryBoolean.optional(),flagged:QueryBoolean.optional(),hasAttachments:QueryBoolean.optional()},ListMessagesQuery=named("ListMessagesQuery",z18.object({mailboxId:Id.optional(),...MessageFilters,importance:MessagePriority.optional(),outboxId:Id.optional(),internetMessageId:z18.string().max(MAX_MESSAGE_ID_LENGTH).regex(/^<[^<>\s]+>$/,"internetMessageId must be one <id>").optional(),after:QueryTimestamp.optional(),before:QueryTimestamp.optional(),include:includeList(MessageListInclude).optional(),limit:z18.coerce.number().int().min(1).max(MAX_PAGE_SIZE).optional(),cursor:Cursor.optional()}));function withListIncludeRules(schema){return schema.superRefine((value,ctx)=>{let{include,limit}=value;if(include?.includes("headers")&&!include.includes("body"))ctx.addIssue({code:"custom",path:["include"],message:"headers needs body in include"});if(include?.includes("body")&&limit!==void 0&&limit>MAX_MESSAGE_BODIES_PAGE_SIZE)ctx.addIssue({code:"custom",path:["limit"],message:`limit is at most ${MAX_MESSAGE_BODIES_PAGE_SIZE} with include=body`})})}var SearchMessagesQuery=named("SearchMessagesQuery",z18.object({q:trimmed(MAX_SEARCH_QUERY_LENGTH),mailboxId:Id.optional(),...MessageFilters,after:QueryTimestamp.optional(),before:QueryTimestamp.optional(),source:SearchSource.default("auto"),limit:pageLimit(MAX_SEARCH_PAGE_SIZE,DEFAULT_SEARCH_PAGE_SIZE),cursor:Cursor.optional()})),UpdateMessage=named("UpdateMessage",z18.object({isRead:z18.boolean().optional(),isFlagged:z18.boolean().optional(),mailboxId:Id.optional(),addMailboxIds:z18.array(Id).max(MAX_MAILBOX_CHANGES).optional(),removeMailboxIds:z18.array(Id).max(MAX_MAILBOX_CHANGES).optional()})),DeleteMessageQuery=named("DeleteMessageQuery",z18.object({permanent:QueryBoolean.default(!1)})),DeleteMessageInput=named("DeleteMessageInput",z18.object({params:MessageIdInput,query:DeleteMessageQuery}));import*as z20 from"zod";import*as z19 from"zod";var GLOBAL_LIST_ID="global";var LIST_ID_PATTERN=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/,SuppressionListId=z19.string().trim().toLowerCase().regex(LIST_ID_PATTERN),ListId=SuppressionListId.refine((id)=>id!==GLOBAL_LIST_ID,{message:`"${GLOBAL_LIST_ID}" is reserved`}),Reason=trimmed(MAX_SUPPRESSION_REASON_LENGTH),ListName=Name.trim().min(1),ListDescription=z19.string().trim().max(MAX_SUPPRESSION_LIST_DESCRIPTION_LENGTH),SuppressionSource=named("SuppressionSource",z19.enum(["unsubscribe","bounce","api","reply","complaint","inactive","invalid"])),DetectedOptOutSource=named("DetectedOptOutSource",SuppressionSource.extract(["reply","complaint","inactive"])),SuppressionEntry=named("SuppressionEntry",z19.object({address:z19.string(),source:SuppressionSource,reason:z19.string().nullable(),createdAt:Timestamp})),SuppressionEntryPage=Page(SuppressionEntry),SuppressionCounts=named("SuppressionCounts",z19.object({unsubscribe:z19.number().int(),bounce:z19.number().int(),api:z19.number().int(),reply:z19.number().int(),complaint:z19.number().int(),inactive:z19.number().int(),invalid:z19.number().int()})),SuppressionList=named("SuppressionList",z19.object({id:z19.string(),name:z19.string(),description:z19.string().nullable(),count:z19.number().int(),counts:SuppressionCounts,createdAt:Timestamp.nullable()})),SuppressionListList=List(SuppressionList),SuppressionReview=named("SuppressionReview",z19.object({id:Id,listId:z19.string(),address:z19.string(),source:DetectedOptOutSource,accountId:Id,messageId:Id,probability:z19.number(),alternateContacts:z19.array(z19.string()),createdAt:Timestamp})),SuppressionReviewPage=Page(SuppressionReview),ListIdInput=named("ListIdInput",z19.object({listId:ListId})),SuppressionListIdInput=named("SuppressionListIdInput",z19.object({listId:SuppressionListId})),ReviewIdInput=named("ReviewIdInput",z19.object({reviewId:Id})),CreateSuppressionList=named("CreateSuppressionList",z19.object({id:ListId,name:ListName,description:ListDescription.nullable().optional()})),UpdateSuppressionList=named("UpdateSuppressionList",z19.object({name:ListName.optional(),description:ListDescription.nullable().optional()})),DeleteSuppressionListQuery=named("DeleteSuppressionListQuery",z19.object({withEntries:QueryBoolean.default(!1)})),DeleteSuppressionListInput=named("DeleteSuppressionListInput",z19.object({params:ListIdInput,query:DeleteSuppressionListQuery})),ListSuppressionEntriesQuery=named("ListSuppressionEntriesQuery",z19.object({address:z19.string().trim().max(MAX_EMAIL_LENGTH).optional(),source:SuppressionSource.optional(),...Paged})),AddSuppressionEntriesRequest=named("AddSuppressionEntriesRequest",z19.object({addresses:z19.array(Email).min(1).max(MAX_SUPPRESSION_BATCH),reason:Reason.optional()})),SuppressionEntryInput=named("SuppressionEntryInput",SuppressionListIdInput.extend({address:trimmed(MAX_EMAIL_LENGTH)})),ListSuppressionReviewsQuery=named("ListSuppressionReviewsQuery",z19.object({listId:SuppressionListId.optional(),...Paged})),SuppressionAdded=named("SuppressionAdded",z19.object({added:z19.boolean()})),SuppressionEntriesAdded=named("SuppressionEntriesAdded",z19.object({added:z19.number().int(),alreadyListed:z19.number().int()}));var AttachmentInput=named("AttachmentInput",z20.object({filename:z20.string().min(1).max(MAX_FILENAME_LENGTH),contentType:z20.string().min(1).max(MAX_CONTENT_TYPE_LENGTH),content:z20.base64(),contentId:z20.string().max(MAX_CONTENT_ID_LENGTH).optional()})),Compose=named("Compose",z20.object({to:z20.array(AddressInput).max(MAX_RECIPIENTS).default([]),cc:z20.array(AddressInput).max(MAX_RECIPIENTS).default([]),bcc:z20.array(AddressInput).max(MAX_RECIPIENTS).default([]),replyTo:z20.array(AddressInput).max(MAX_REPLY_TO).default([]),subject:z20.string().max(MAX_SUBJECT_LENGTH).optional(),text:z20.string().max(MAX_BODY_LENGTH).optional(),html:z20.string().max(MAX_BODY_LENGTH).optional(),attachments:z20.array(AttachmentInput).max(MAX_ATTACHMENTS).default([]),inReplyToMessageId:Id.optional(),from:AddressInput.optional(),signature:z20.boolean().optional(),priority:MessagePriority.default("normal")})),ComposeWithTemplate=named("ComposeWithTemplate",Compose.extend({template:Id.optional(),render:z20.object({params:RenderParams.default({})}).optional(),previewText:z20.string().max(MAX_TEMPLATE_PREVIEW_TEXT_LENGTH).optional(),format:TemplateFormat.optional()})),OutboxStatus=named("OutboxStatus",z20.enum(["queued","sending","sent","failed","cancelled"])),OutboxEntry=named("OutboxEntry",z20.object({id:Id,accountId:Id,status:OutboxStatus,reference:z20.string().nullable(),sendAt:Timestamp,attempt:z20.number().int(),lastError:z20.string().nullable(),providerMessageId:ProviderId.nullable(),internetMessageId:z20.string().nullable(),sentAt:Timestamp.nullable(),sentMessageId:Id.nullable(),threadId:ProviderId.nullable(),createdAt:Timestamp,trackOpens:z20.boolean(),trackClicks:z20.boolean(),trackHoneypot:z20.boolean(),humanOpenCount:z20.number().int(),machineOpenCount:z20.number().int(),humanClickCount:z20.number().int(),machineClickCount:z20.number().int(),firstOpenedAt:Timestamp.nullable(),lastOpenedAt:Timestamp.nullable(),mailMergeId:Id.nullable()})),QueuedSend=named("QueuedSend",OutboxEntry.extend({undoUntil:Timestamp.nullable()})),SendTracking=named("SendTracking",z20.object({opens:z20.boolean().optional(),clicks:z20.boolean().optional(),honeypot:z20.boolean().optional()})),SendOptions=named("SendOptions",z20.object({sendAt:QueryTimestamp.optional(),reference:z20.string().max(MAX_SEND_REFERENCE_LENGTH).optional(),tracking:SendTracking.optional()})),ListOption={listId:ListId.optional()},MESSAGE_ID_PATTERN=/^<[^<>\s@]+@[^<>\s@]+>$/,SEND_HEADER_NAMES=["List-Unsubscribe","List-Unsubscribe-Post","List-Id","List-Help","List-Subscribe","List-Post","List-Owner","List-Archive","Auto-Submitted","Precedence","In-Reply-To","References","Feedback-ID","Keywords","Disposition-Notification-To"],ALLOWED_HEADERS=Object.fromEntries(SEND_HEADER_NAMES.map((name)=>[name.toLowerCase(),!0])),REFUSED_X_HEADERS={"x-priority":!0,"x-msmail-priority":!0};function isSendHeaderAllowed(name){let lower=name.toLowerCase();if(Object.hasOwn(ALLOWED_HEADERS,lower))return!0;return lower.startsWith("x-")&&!lower.startsWith("x-workwae-")&&!Object.hasOwn(REFUSED_X_HEADERS,lower)}function hasControlCharacters(value){return/[\x00-\x1f\x7f]/.test(value)}var SendHeaders=named("SendHeaders",z20.record(z20.string().regex(/^[A-Za-z0-9-]{1,76}$/,"Header names are letters, digits and hyphens"),z20.string().min(1).max(MAX_HEADER_VALUE_LENGTH).refine((value)=>!hasControlCharacters(value),"Header values must not contain control characters")).superRefine((headers,ctx)=>{let names=Object.keys(headers);if(names.length>MAX_SEND_HEADERS)ctx.addIssue({code:"custom",message:`At most ${MAX_SEND_HEADERS} headers`});let seen=new Set;for(let name of names){if(!isSendHeaderAllowed(name))ctx.addIssue({code:"custom",path:[name],message:`${name} cannot be set: use an X- header or one of ${SEND_HEADER_NAMES.join(", ")}`});let lower=name.toLowerCase();if(seen.has(lower))ctx.addIssue({code:"custom",path:[name],message:`${name} is given twice`});seen.add(lower)}})),CalendarInvite=named("CalendarInvite",z20.object({method:z20.enum(["PUBLISH","REQUEST","REPLY","ADD","CANCEL","REFRESH","COUNTER","DECLINECOUNTER"]),content:z20.string().max(MAX_CALENDAR_LENGTH).regex(/^\s*BEGIN:VCALENDAR/i,"content must be an iCalendar object (BEGIN:VCALENDAR)")})),SingleSendOptions={messageId:z20.string().max(MAX_MESSAGE_ID_LENGTH).regex(MESSAGE_ID_PATTERN,"messageId must be one <local@domain>").optional(),headers:SendHeaders.optional(),calendar:CalendarInvite.optional(),wait:z20.number().int().min(1).max(MAX_SEND_WAIT_SECONDS).optional()},MailMergeEntry=named("MailMergeEntry",z20.object({to:AddressInput.extend({address:z20.string().max(MAX_EMAIL_LENGTH)}),params:RenderParams.optional(),messageId:z20.string().max(MAX_MESSAGE_ID_LENGTH).optional(),sendAt:QueryTimestamp.optional()})),SendMessageRequest=named("SendMessageRequest",ComposeWithTemplate.extend(SendOptions.shape).extend(ListOption).extend(SingleSendOptions)),MailMergeRequest=named("MailMergeRequest",ComposeWithTemplate.omit({to:!0,cc:!0,bcc:!0,render:!0}).extend(SendOptions.shape).extend({...ListOption,entries:z20.array(MailMergeEntry).min(1).max(MAX_MAIL_MERGE_ENTRIES)}).strict()),MailMergeSuccess=named("MailMergeSuccess",z20.object({success:z20.literal(!0),to:Address,outboxId:Id,messageId:z20.string(),sendAt:Timestamp,undoUntil:Timestamp.nullable()})),MailMergeFailure=named("MailMergeFailure",z20.object({success:z20.literal(!1),to:Address,reason:z20.string(),error:z20.string(),status:z20.number().int()})),MailMergeResult=named("MailMergeResult",z20.discriminatedUnion("success",[MailMergeSuccess,MailMergeFailure])),MailMergeResultList=named("MailMergeResultList",z20.object({id:Id.nullable(),...listFields(MailMergeResult)})),MailMerge=named("MailMerge",z20.object({id:Id,accountId:Id,reference:z20.string().nullable(),entryCount:z20.number().int(),createdAt:Timestamp,counts:z20.object({queued:z20.number().int(),sending:z20.number().int(),sent:z20.number().int(),failed:z20.number().int(),cancelled:z20.number().int()})})),MailMergeIdInput=named("MailMergeIdInput",AccountIdInput.extend({mailMergeId:Id})),MailMergePage=Page(MailMerge),ListMailMergesQuery=named("ListMailMergesQuery",z20.object(Paged)),MailMergeCancelled=named("MailMergeCancelled",z20.object({cancelled:z20.number().int()})),OutboxIdInput=named("OutboxIdInput",AccountIdInput.extend({outboxId:Id})),OutboxEntryPage=Page(OutboxEntry),ListOutboxQuery=named("ListOutboxQuery",z20.object({status:OutboxStatus.optional(),...Paged})),RescheduleSendRequest=named("RescheduleSendRequest",z20.object({sendAt:QueryTimestamp})),ListTrackingEventsQuery=named("ListTrackingEventsQuery",z20.object(Paged)),TrackingEventKind=named("TrackingEventKind",z20.enum(["open","click"])),TrackingActor=named("TrackingActor",z20.enum(["human","proxy","prefetch","scanner","unknown"])),TrackingClient=named("TrackingClient",z20.enum(["gmail_proxy","gmail_web","gmail_app","outlook_web","outlook_desktop","outlook_mobile","apple_mail","apple_mail_privacy","yahoo","aol","thunderbird","proton","fastmail","zoho","icloud_web","webmail","unknown"])),TrackingDevice=named("TrackingDevice",z20.enum(["desktop","mobile","tablet","bot","unknown"])),TrackingOs=named("TrackingOs",z20.enum(["windows","macos","ios","ipados","android","linux","chromeos"])),TrackingBrowser=named("TrackingBrowser",z20.enum(["chrome","edge","firefox","safari","opera","samsung"])),TrackingSecFetch=named("TrackingSecFetch",z20.object({dest:z20.string().nullable(),mode:z20.string().nullable(),site:z20.string().nullable(),user:z20.boolean().nullable()})),TrackingSignals=named("TrackingSignals",z20.object({method:z20.string(),refererHost:z20.string().nullable(),originHost:z20.string().nullable(),client:TrackingClient,device:TrackingDevice,os:TrackingOs.nullable(),browser:TrackingBrowser.nullable(),country:z20.string().nullable(),region:z20.string().nullable(),city:z20.string().nullable(),timezone:z20.string().nullable(),latitude:z20.number().nullable(),longitude:z20.number().nullable(),secondsAfterSend:z20.number().nullable(),secondsSincePrevious:z20.number().nullable(),sequence:z20.number().int(),isConditional:z20.boolean(),isRange:z20.boolean(),isPrefetch:z20.boolean(),secFetch:TrackingSecFetch})),TrackingEvent=named("TrackingEvent",z20.object({id:Id,kind:TrackingEventKind,linkIndex:z20.number().int().nullable(),url:z20.string().nullable(),occurredAt:Timestamp,userAgent:z20.string().nullable(),ip:z20.string().nullable(),actor:TrackingActor,reasons:z20.array(z20.string()),isMachine:z20.boolean(),...TrackingSignals.shape,headers:z20.record(z20.string(),z20.string())})),TrackingEventPage=Page(TrackingEvent);var AccountConnectedData=named("AccountConnectedData",z21.object({email:z21.string(),provider:AccountProvider,cloud:MicrosoftCloud.nullable(),externalRef:z21.string().nullable(),reauthorized:z21.boolean()})),AccountUpdatedField=named("AccountUpdatedField",z21.enum(["status","externalRef","settings","imap","dav","credentials"])),AccountUpdatedData=named("AccountUpdatedData",z21.object({fields:z21.array(AccountUpdatedField)})),AccountDeletedData=named("AccountDeletedData",z21.object({email:z21.string(),externalRef:z21.string().nullable(),purge:z21.boolean()})),AccountReauthRequiredData=named("AccountReauthRequiredData",AccountConnectedData.omit({reauthorized:!0}).extend({reason:z21.string()})),AccountInitializedData=named("AccountInitializedData",z21.object({mailboxes:z21.number().int(),messages:z21.number().int()})),AccountPurgedData=named("AccountPurgedData",z21.object({accountId:Id,externalRef:z21.string().nullable()})),MailboxData=named("MailboxData",z21.object({mailbox:Mailbox})),MailboxUpdatedData=named("MailboxUpdatedData",MailboxData.extend({changes:z21.array(MailboxChangedField)})),MailboxRef=named("MailboxRef",Mailbox.pick({id:!0,name:!0,role:!0})),MessageData=named("MessageData",z21.object({message:MessageSummary,mailboxes:z21.array(MailboxRef)})),MessageUpdatedData=named("MessageUpdatedData",MessageData.extend({changes:z21.array(MessageChangedField)})),MessageDeletedData=named("MessageDeletedData",MessageData.extend({messageId:Id,providerMessageId:ProviderId})),MessageClassifiedData=named("MessageClassifiedData",MessageData.extend({classification:MessageClassification})),BounceRecipient=named("BounceRecipient",z21.object({address:z21.string(),status:z21.string().nullable(),diagnostic:z21.string().nullable()})),MessageBouncedData=named("MessageBouncedData",z21.object({messageId:Id,outboxId:Id.nullable(),reference:z21.string().nullable(),originalMessageId:z21.string().nullable(),recipients:z21.array(BounceRecipient)})),TrackingHitData=named("TrackingHitData",TrackingEvent.omit({id:!0,kind:!0,linkIndex:!0,ip:!0,headers:!0}).extend({outboxId:Id,reference:z21.string().nullable(),messageId:Id.nullable()})),SendError=named("SendError",z21.object({code:z21.string(),message:z21.string()})),SendFields={outboxId:Id,reference:z21.string().nullable()},RejectedRecipient=named("RejectedRecipient",z21.object({address:z21.string(),status:z21.string().nullable(),diagnostic:z21.string().nullable()})),SendSentData=named("SendSentData",z21.object({...SendFields,attempt:z21.number().int(),providerMessageId:ProviderId.nullable(),internetMessageId:z21.string().nullable(),sentAt:Timestamp,rejectedRecipients:z21.array(RejectedRecipient)})),SendRetryingData=named("SendRetryingData",z21.object({...SendFields,attempt:z21.number().int(),nextAttemptAt:Timestamp,error:SendError})),SendFailedData=named("SendFailedData",z21.object({...SendFields,attempt:z21.number().int(),error:SendError})),SendRescheduledData=named("SendRescheduledData",z21.object({...SendFields,sendAt:Timestamp,previousSendAt:Timestamp})),SendCancelledData=named("SendCancelledData",z21.object({...SendFields,mailMergeId:Id.nullable()})),SuppressionCreatedData=named("SuppressionCreatedData",z21.object({listId:z21.string(),address:z21.string(),source:SuppressionSource})),CalendarInitializedData=named("CalendarInitializedData",z21.object({calendars:z21.number().int(),events:z21.number().int()})),CalendarData=named("CalendarData",z21.object({calendar:Calendar})),CalendarUpdatedData=named("CalendarUpdatedData",CalendarData.extend({changes:z21.array(CalendarChangedField)})),CalendarEventData=named("CalendarEventData",z21.object({event:CalendarEvent})),CalendarEventUpdatedData=named("CalendarEventUpdatedData",CalendarEventData.extend({changes:z21.array(CalendarEventChangedField)})),CalendarEventDeletedData=named("CalendarEventDeletedData",z21.object({eventId:Id,calendarId:Id,kind:EventKind,seriesId:Id.nullable()})),ContactsInitializedData=named("ContactsInitializedData",z21.object({contacts:z21.number().int()})),ContactData=named("ContactData",z21.object({contact:Contact})),ContactUpdatedData=named("ContactUpdatedData",ContactData.extend({changes:z21.array(ContactChangedField)})),ContactDeletedData=named("ContactDeletedData",z21.object({contactId:Id,folderId:Id.nullable()})),WebhookTestData=named("WebhookTestData",z21.object({endpointId:Id})),ExportCompletedData=named("ExportCompletedData",z21.object({exportId:Id,bytes:z21.number().int(),sha256:z21.string(),expiresAt:Timestamp})),ExportFailedData=named("ExportFailedData",z21.object({exportId:Id,error:z21.string()})),MeetingConnectionConnectedData=named("MeetingConnectionConnectedData",z21.object({service:MeetingService,email:z21.string(),reconnected:z21.boolean()})),MeetingConnectionReauthRequiredData=named("MeetingConnectionReauthRequiredData",MeetingConnectionConnectedData.omit({reconnected:!0}).extend({reason:z21.string()})),BookingEventData=named("BookingEventData",z21.object({bookingId:Id,linkId:Id,eventId:Id.nullable(),start:Timestamp,end:Timestamp,invitee:z21.string()}));function defineEvent(scope,data,description){return{scope,data,description}}var EVENTS={"account.connected":defineEvent("account",AccountConnectedData,"An account was connected, or an existing account signed in again (reauthorized)."),"account.updated":defineEvent("account",AccountUpdatedData,"An account's status, externalRef, settings or connection changed."),"account.deleted":defineEvent("account",AccountDeletedData,"An account was deleted; its data is purged after the retention period, or at once with purge."),"account.reauth_required":defineEvent("account",AccountReauthRequiredData,"The provider refused the account's grant: the account must sign in again."),"account.initialized":defineEvent("account",AccountInitializedData,"The account's first mail sync finished; nothing before it is announced message by message."),"account.purged":defineEvent("tenant",AccountPurgedData,"A deleted account's data is gone for good (retention, or an immediate purge). The event names no account, since it outlives the account it reports on."),"mailbox.created":defineEvent("account",MailboxData,"A mailbox (folder or label) appeared."),"mailbox.updated":defineEvent("account",MailboxUpdatedData,"A mailbox was renamed, moved or given another role. Counts change silently."),"mailbox.deleted":defineEvent("account",MailboxData,"A mailbox went away."),"message.created":defineEvent("account",MessageData,"A message was indexed: received, sent, drafted or moved in from outside the sync window."),"message.updated":defineEvent("account",MessageUpdatedData,"A message changed: read, flagged, moved, or linked to the outbox entry it is a copy of."),"message.deleted":defineEvent("account",MessageDeletedData,"A message was removed; carries it and its mailboxes as last indexed."),"message.bounced":defineEvent("account",MessageBouncedData,"A delivery status notification reporting failed recipients reached the account; carries the outbox id when it answers one of our sends."),"message.classified":defineEvent("account",MessageClassifiedData,"AI classified a reply to one of our sends (tenant setting aiReplyClassification): carries the message, its mailboxes and the classification."),"message.opened":defineEvent("account",TrackingHitData,"A recipient's client (or a proxy fetching for it) loaded the open-tracking pixel of a send. Carries the actor, its reasons and the derived signals (client, device, location, referer host, sec-fetch tuple, timing); the raw headers are only on the tracking list."),"message.clicked":defineEvent("account",TrackingHitData,"A tracked link of a send was followed; carries the same as message.opened."),"send.sent":defineEvent("account",SendSentData,"The provider accepted a queued send."),"send.retrying":defineEvent("account",SendRetryingData,"An attempt failed and the send is queued again for nextAttemptAt."),"send.failed":defineEvent("account",SendFailedData,"The send failed for good: refused, or out of attempts."),"send.rescheduled":defineEvent("account",SendRescheduledData,"A queued send got a new sendAt before its first attempt."),"send.cancelled":defineEvent("account",SendCancelledData,"A queued send was cancelled before it went out, on its own or with its mail merge."),"suppression.created":defineEvent("account",SuppressionCreatedData,"An address went onto a suppression list: the recipient unsubscribed (source unsubscribe, listId the send's), a permanent bounce said the address is gone (source bounce, listId global), or a detected opt-out was suppressed, at once or on approval (source reply or complaint, listId the send's or global; source inactive, on approval only, listId global). Only a change is announced; API adds are audited instead. The account is the one whose send it concerns."),"calendar.initialized":defineEvent("account",CalendarInitializedData,"The account's first calendar sync finished; nothing before it is announced event by event."),"calendar.created":defineEvent("account",CalendarData,"A calendar appeared."),"calendar.updated":defineEvent("account",CalendarUpdatedData,"A calendar changed."),"calendar.deleted":defineEvent("account",CalendarData,"A calendar went away."),"calendar_event.created":defineEvent("account",CalendarEventData,"A single event, series or exception appeared. Unmodified occurrences of a series are never announced; an occurrence changed on its own is announced as a new exception."),"calendar_event.updated":defineEvent("account",CalendarEventUpdatedData,"A single event, series or exception changed; a change to a series is one update of the series."),"calendar_event.deleted":defineEvent("account",CalendarEventDeletedData,"A single event, series or exception went away."),"contacts.initialized":defineEvent("account",ContactsInitializedData,"The account's first contacts sync finished; nothing before it is announced contact by contact."),"contact.created":defineEvent("account",ContactData,"A contact appeared."),"contact.updated":defineEvent("account",ContactUpdatedData,"A contact changed."),"contact.deleted":defineEvent("account",ContactDeletedData,"A contact went away."),"export.completed":defineEvent("either",ExportCompletedData,"A data export finished and can be downloaded until data.expiresAt. accountId is null for an export of the whole tenant."),"export.failed":defineEvent("either",ExportFailedData,"A data export failed. accountId is null for an export of the whole tenant."),"meeting_connection.connected":defineEvent("account",MeetingConnectionConnectedData,"An account connected (or reconnected) its own grant at a meeting service (Zoom, Webex)."),"meeting_connection.reauth_required":defineEvent("account",MeetingConnectionReauthRequiredData,"The service refused the account's meeting grant; meetings need a new connect. Mail and calendar sync go on."),"booking.created":defineEvent("account",BookingEventData,"A scheduling link was booked: the account hosts the event, and the invitee is an attendee."),"booking.cancelled":defineEvent("account",BookingEventData,"A booking was cancelled and its event removed."),"webhook.test":defineEvent("tenant",WebhookTestData,"Sent by the test action to the endpoint it names, whatever that endpoint's filters.")},EVENT_TYPES=Object.keys(EVENTS),EventType=named("EventType",z21.enum(EVENT_TYPES)),TEST_EVENT_TYPE="webhook.test";var ENVELOPE_ACCOUNT_ID={account:Id,tenant:z21.null(),either:Id.nullable()};function envelope(type){let{scope,data,description}=EVENTS[type],name=type.replace(/(?:^|[._])([a-z])/g,(_,letter)=>letter.toUpperCase());return named(`${name}Event`,z21.object({id:EventId,type:z21.literal(type),accountId:ENVELOPE_ACCOUNT_ID[scope],createdAt:Timestamp,data,isTest:z21.literal(!0).exactOptional()}).meta({description}))}var Event=named("Event",z21.discriminatedUnion("type",EVENT_TYPES.map(envelope))),ListChangesQuery=named("ListChangesQuery",z21.object({cursor:Cursor.optional(),limit:Limit,types:z21.union([z21.string(),z21.array(EventType)]).transform((v)=>Array.isArray(v)?v:v.split(",")).pipe(z21.array(EventType)).optional()})),EventFeedPage=named("EventFeedPage",z21.object({items:z21.array(Event),nextCursor:Cursor}));import*as z22 from"zod";var ExportStatus=named("ExportStatus",z22.enum(["queued","running","completed","failed","expired"])),DataExport=named("DataExport",z22.object({id:Id,accountId:Id.nullable(),accountEmail:z22.string().nullable(),status:ExportStatus,bytes:z22.number().int(),sha256:z22.string().nullable(),contents:z22.record(z22.string(),z22.number().int()),error:z22.string().nullable(),createdAt:Timestamp,startedAt:Timestamp.nullable(),completedAt:Timestamp.nullable(),expiresAt:Timestamp.nullable()})),DataExportPage=Page(DataExport),CreateDataExport=named("CreateDataExport",z22.object({accountId:Id.optional()})),ListDataExportsQuery=named("ListDataExportsQuery",z22.object(Paged)),DataExportIdInput=named("DataExportIdInput",z22.object({exportId:Id}));import*as z23 from"zod";var IdentitySource=named("IdentitySource",z23.enum(["primary","provider","custom"])),Signature=named("Signature",z23.object({html:z23.string().nullable(),text:z23.string().nullable()})),SignatureInput=named("SignatureInput",z23.object({html:z23.string().max(MAX_SIGNATURE_HTML_LENGTH).optional(),text:z23.string().max(MAX_SIGNATURE_TEXT_LENGTH).optional()}).refine((s)=>s.html!==void 0||s.text!==void 0,{message:"A signature needs html or text"})),Identity=named("Identity",Address.extend({id:Id,source:IdentitySource,signature:Signature.nullable(),providerSignature:Signature.nullable()})),IdentityList=named("IdentityList",z23.object({...listFields(Identity),refreshedAt:Timestamp.nullable()})),MailSettings=named("MailSettings",z23.object({signByDefault:z23.boolean()})),UpdateMailSettings=named("UpdateMailSettings",MailSettings.partial()),IdentityIdInput=named("IdentityIdInput",AccountIdInput.extend({identityId:Id})),ListIdentitiesQuery=named("ListIdentitiesQuery",z23.object({refresh:QueryBoolean.optional()})),CreateIdentity=named("CreateIdentity",AddressInput.extend({signature:SignatureInput.optional()})),UpdateIdentity=named("UpdateIdentity",z23.object({name:z23.string().max(MAX_ADDRESS_NAME_LENGTH).nullable().optional(),signature:SignatureInput.nullable().optional()}));import*as z24 from"zod";var PUBSUB_TOPIC_PATTERN=/^projects\/[a-z][a-z0-9.:-]{4,61}[a-z0-9]\/topics\/(?!goog)[A-Za-z][A-Za-z0-9._~+%-]{2,254}$/,GmailPushSettings=named("GmailPushSettings",z24.object({topic:z24.string().regex(PUBSUB_TOPIC_PATTERN,"Expected projects/<project>/topics/<topic>"),serviceAccount:z24.email().regex(/\.gserviceaccount\.com$/i,"Expected a Google service account email")})),OAuthApp=named("OAuthApp",z24.object({id:Id,provider:OAuthProvider,name:z24.string(),clientId:z24.string(),cloud:MicrosoftCloud.nullable(),authority:z24.string().nullable(),useConnectDomain:z24.boolean(),redirectUri:z24.url(),gmailPush:GmailPushSettings.nullable(),gmailPushEndpoint:z24.url().nullable(),createdAt:Timestamp})),OAuthAppList=List(OAuthApp),CreateOAuthApp=named("CreateOAuthApp",z24.object({provider:OAuthProvider,cloud:MicrosoftCloud.optional(),name:Name,clientId:z24.string().min(1),clientSecret:z24.string().min(1),authority:z24.string().optional(),useConnectDomain:z24.boolean().optional()})),UpdateOAuthApp=named("UpdateOAuthApp",z24.object({name:Name.optional(),clientSecret:z24.string().min(1).optional(),authority:z24.string().min(1).optional(),gmailPush:GmailPushSettings.nullable().optional(),useConnectDomain:z24.boolean().optional()})),OAuthAppIdInput=named("OAuthAppIdInput",z24.object({appId:Id}));import*as z25 from"zod";var OrgConnectionKind=named("OrgConnectionKind",z25.enum(["google_delegation","microsoft_app"])),OrgConnectionStatus=named("OrgConnectionStatus",z25.enum(["pending_consent","unverified","active","failing"])),MicrosoftAppCredential=named("MicrosoftAppCredential",z25.enum(["platform","client_secret","certificate"])),GoogleDelegation=named("GoogleDelegation",z25.object({domain:z25.string(),serviceAccountEmail:z25.string(),directoryAdmin:z25.string().nullable(),scopes:z25.array(z25.string()),groupsEnabled:z25.boolean()})),GraphPermission=named("GraphPermission",z25.object({name:z25.string(),isRequired:z25.boolean()})),MicrosoftAppAccess=named("MicrosoftAppAccess",z25.object({cloud:MicrosoftCloud,credentialType:MicrosoftAppCredential,directoryId:z25.string().nullable(),useConnectDomain:z25.boolean(),redirectUri:z25.string().nullable(),permissions:z25.array(GraphPermission)})),OrgConnection=named("OrgConnection",z25.object({id:Id,kind:OrgConnectionKind,name:z25.string(),clientId:z25.string(),status:OrgConnectionStatus,statusDetail:z25.string().nullable(),checkedAt:Timestamp.nullable(),google:GoogleDelegation.nullable(),microsoft:MicrosoftAppAccess.nullable(),accounts:z25.number().int(),createdAt:Timestamp,updatedAt:Timestamp})),OrgConnectionList=List(OrgConnection),Pem=z25.string().min(1).max(MAX_KEY_FILE_LENGTH),ServiceAccountKey=z25.string().min(2).max(MAX_KEY_FILE_LENGTH),EntraId=z25.string().min(1).max(MAX_ENTRA_ID_LENGTH),ClientSecret2=z25.string().min(1).max(MAX_SECRET_LENGTH),CreateOrgConnection=named("CreateOrgConnection",z25.discriminatedUnion("kind",[z25.object({kind:z25.literal("google_delegation"),name:Name,serviceAccountKey:ServiceAccountKey,domain:z25.string().min(3).max(MAX_HOST_LENGTH),calendarEnabled:z25.boolean().default(!0),contactsEnabled:z25.boolean().default(!1),directoryAdmin:Email.optional(),groupsEnabled:z25.boolean().default(!1)}),z25.object({kind:z25.literal("microsoft_app"),name:Name,cloud:MicrosoftCloud.default("global"),credential:z25.discriminatedUnion("type",[z25.object({type:z25.literal("platform")}),z25.object({type:z25.literal("client_secret"),clientId:EntraId,clientSecret:ClientSecret2}),z25.object({type:z25.literal("certificate"),clientId:EntraId,certificate:Pem,privateKey:Pem})]),directoryId:EntraId.optional(),useConnectDomain:z25.boolean().optional()})])),UpdateOrgConnection=named("UpdateOrgConnection",z25.object({name:Name.optional(),serviceAccountKey:ServiceAccountKey.optional(),calendarEnabled:z25.boolean().optional(),contactsEnabled:z25.boolean().optional(),groupsEnabled:z25.boolean().optional(),directoryAdmin:Email.nullable().optional(),clientSecret:ClientSecret2.optional(),certificate:Pem.optional(),privateKey:Pem.optional(),directoryId:EntraId.optional(),useConnectDomain:z25.boolean().optional()})),OrgConnectionCheck=named("OrgConnectionCheck",z25.object({name:z25.string(),isOk:z25.boolean(),isRequired:z25.boolean(),detail:z25.string()})),OrgConnectionTestResult=named("OrgConnectionTestResult",z25.object({connection:OrgConnection,checks:z25.array(OrgConnectionCheck)})),DirectoryEntry=named("DirectoryEntry",z25.object({id:ProviderId,email:z25.string(),displayName:z25.string().nullable(),accountId:Id.nullable()})),DirectoryEntryPage=Page(DirectoryEntry),AddMailboxInput=named("AddMailboxInput",z25.object({email:Email,displayName:z25.string().max(MAX_DISPLAY_NAME_LENGTH).optional(),externalRef:ExternalRef.optional()})),AddMailboxesRequest=named("AddMailboxesRequest",z25.object({mailboxes:z25.array(AddMailboxInput).min(1).max(MAX_ADD_MAILBOXES),settings:SettingsOverride.optional()})),AddedMailboxStatus=named("AddedMailboxStatus",z25.enum(["created","exists","failed"])),AddedMailboxError=named("AddedMailboxError",z25.object({reason:z25.string(),message:z25.string()})),AddedMailbox=named("AddedMailbox",z25.object({email:z25.string(),status:AddedMailboxStatus,accountId:Id.nullable(),error:AddedMailboxError.nullable()})),AddedMailboxList=List(AddedMailbox),OrgConnectionIdInput=named("OrgConnectionIdInput",z25.object({connectionId:Id})),StartConsentRequest=named("StartConsentRequest",z25.object({redirectUri:z25.url(),state:RedirectState.optional()})),ConsentLink=named("ConsentLink",z25.object({consentUrl:z25.url()})),TestOrgConnectionRequest=named("TestOrgConnectionRequest",z25.object({user:Email.optional()})),ListDirectoryEntriesQuery=named("ListDirectoryEntriesQuery",z25.object({cursor:Cursor.optional(),limit:pageLimit(MAX_DIRECTORY_PAGE_SIZE,DEFAULT_DIRECTORY_PAGE_SIZE),query:z25.string().max(MAX_DIRECTORY_QUERY_LENGTH).optional()})),OpenSharedMailboxRequest=named("OpenSharedMailboxRequest",z25.object({email:Email,displayName:z25.string().max(MAX_DISPLAY_NAME_LENGTH).optional(),externalRef:ExternalRef.optional(),settings:SettingsOverride.optional()})),AddGroupRequest=named("AddGroupRequest",z25.object({email:Email,relayEmail:Email,displayName:z25.string().max(MAX_DISPLAY_NAME_LENGTH).optional(),externalRef:ExternalRef.optional(),settings:SettingsOverride.optional()}));import*as z26 from"zod";var MAIL_SETTINGS_CHECK=errors({login_failed:"A server (IMAP, SMTP, CalDAV or CardDAV) refuses the login.",server_not_allowed:"A server resolves to a private or loopback address.",plaintext_not_allowed:"A server is named with security none (a plaintext login)."}),AUTODISCOVERY_FAILED=errors({autodiscovery_failed:"imap was left out and autodiscovery found no IMAP server for the address."}),DAV_SETTINGS_CHECK=errors({dav_discovery_failed:"Neither a CalDAV nor a CardDAV server was found or answered.",not_dav_server:"A named CalDAV or CardDAV URL is no DAV server (it answers 404, 403, or not as one)."}),OAUTH_CLIENT=errors({unknown_oauth_app:"oauthAppId names no OAuth app of the tenant.",oauth_app_mismatch:"The OAuth app is for the other provider.",oauth_app_secret_unreadable:"The OAuth app's client secret cannot be decrypted; replace it.",provider_not_configured:"No OAuth app is given and the platform has none for the provider (and cloud)."}),accounts={list:base.route({method:"GET",path:"/v1/accounts",tags:["Accounts"],summary:"List accounts",description:"Newest first. q matches part of the email address or the external reference, case-insensitively; externalRef matches it exactly. An API key bound to accounts lists only those."}).errors(INVALID_CURSOR).input(ListAccountsQuery).output(AccountPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}",tags:["Accounts"],summary:"Get an account"}).errors(ACCOUNT_NOT_FOUND).input(AccountIdInput).output(Account),create:base.route({method:"POST",path:"/v1/accounts",tags:["Accounts"],summary:"Connect an account from an existing refresh token",description:"The address is the provider's (Google's userinfo, Graph's /me). The token must cover the calendar and contacts that settings.calendarEnabled and settings.contactsEnabled name as true. Where settings leaves one out and the workspace default is on, a Google token that already carries its scopes gets it, and any other token connects with it off for this account (a Microsoft token is refreshed only for the scopes asked, so name the setting to require them).",successStatus:201}).errors({...bodyStorage("settings.bodyStorage"),...planModule("shared mailboxes (sharedMailboxesEnabled: true)"),...PLAN_LIMIT_ACCOUNTS,...BILLING_PAST_DUE,...OAUTH_CLIENT,...errors({invalid_grant:"The provider refuses the refresh token (revoked, expired or never valid).",invalid_client:"The provider refuses the OAuth app's client credentials.",interaction_required:"The provider needs the user to sign in again (new MFA or Conditional Access requirement, or consent).",token_exchange_failed:"The provider's token endpoint refuses the refresh for another reason.",scope_denied:"The grant lacks a scope the account needs.",access_token_rejected:"The provider refuses the access token the refresh returned.",identity_failed:"The provider's identity answer names no address.",identity_unavailable:"Google has not verified the account's address.",provider_unavailable:"The provider's token or identity endpoint could not be reached."})}).input(CreateAccount).output(Account),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}",tags:["Accounts"],summary:"Update an account",description:"Changing settings.bodyStorage, mailEnabled, calendarEnabled, contactsEnabled or syncWindowDays restarts the account's syncs: full stores every indexed message and backfills, none deletes stored content. status only moves between active and paused; only a new grant clears reauth_required and disabled."}).errors({...ACCOUNT_NOT_FOUND,...PLAN_LIMIT_ACCOUNTS,...bodyStorage("settings.bodyStorage"),...errors({account_reauth_required:"status is changed on an account that must be reconnected.",account_disabled:"status is changed on a disabled account."})}).input(AccountIdInput.extend(UpdateAccount.shape)).output(Account),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}",tags:["Accounts"],summary:"Delete an account",description:"Stops syncing and removes the credential at once. The account's data is purged for good after the retention period (default 30 days), or shortly after the call with ?purge=true; account.purged announces it. Its events are left to age out of the event log (within 31 days). purge=true also purges an account that is already deleted.",successStatus:204,inputStructure:"detailed"}).errors(errors({account_not_found:"The account does not exist, or (without purge) is already deleted."})).input(DeleteAccountInput).output(z26.void()),sync:base.route({method:"POST",path:"/v1/accounts/{accountId}/sync",tags:["Accounts"],summary:"Sync an account now",description:"Wakes the account's sync the way a provider push does: one nudge per kind to the instance that owns it. The owner keeps at most one pending pass per kind, so a repeat while a pass is running becomes a single extra pass. At most 30 of these requests per account per minute (429 rate_limited). kinds defaults to every kind the account has enabled (each of mail, calendar and contacts its settings turn on). With no live owner the answer is still 202: the next owner starts with a full pass, and the backstop poll covers a wake nobody was listening for.",successStatus:202}).errors({...ACCOUNT_NOT_FOUND,...errors({account_reauth_required:"The account must be reconnected before it can sync.",account_disabled:"The account is disabled.",sync_kinds_disabled:"kinds names a sync the account does not run; data.kinds lists those."})}).input(AccountIdInput.extend(SyncAccountRequest.shape)).output(AccountSyncQueued),mintToken:base.route({method:"POST",path:"/v1/accounts/{accountId}/tokens",tags:["Accounts"],summary:"Mint an account-scoped token",description:"The token is returned once; it is not retrievable later."}).errors(ACCOUNT_NOT_FOUND).input(AccountIdInput.extend(MintAccountTokenRequest.shape)).output(AccountToken)},imapAccounts={create:base.route({method:"POST",path:"/v1/imap-accounts",tags:["Accounts"],summary:"Connect an IMAP/SMTP account with a password",description:"The login is checked before anything is stored, for DAV too. Giving dav switches settings.calendarEnabled and contactsEnabled on for the halves found unless settings names them.",successStatus:201}).errors({...MAIL_SETTINGS_CHECK,...AUTODISCOVERY_FAILED,...DAV_SETTINGS_CHECK,...PROVIDER_FAILURES,...bodyStorage("settings.bodyStorage"),...PLAN_LIMIT_ACCOUNTS,...BILLING_PAST_DUE,...planModule("mail (an IMAP account is a mail account)")}).input(CreateImapAccount).output(Account)},imapSettings={update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/imap-settings",tags:["Accounts"],summary:"Change an IMAP account's servers or password",description:"Present fields replace the stored ones and the result is checked like a new account. An account in reauth_required becomes active again. A stored password is never sent to a new server or tried for another login: changing the IMAP server (host, port, security or user) needs password in the same request, changing the SMTP server (host, port, security or user) needs smtpPassword (or password when SMTP uses the IMAP password), switching SMTP from its own password to the IMAP one (smtpPassword: null) needs password, and adding DAV settings or changing their URLs, user or password source needs the password DAV logs in with (dav.password, or password when DAV uses the IMAP password). Sending dav: null removes the calendar and contacts; a CalDAV or CardDAV half the account did not have before switches settings.calendarEnabled or contactsEnabled on, unless the account's settings already name that key (an earlier opt-out stays); dav fields left out keep their stored values, or are discovered for an account without DAV settings. An account that signs in with OAuth always uses its provider's servers."}).errors({...ACCOUNT_NOT_FOUND,...MAIL_SETTINGS_CHECK,...DAV_SETTINGS_CHECK,...PROVIDER_FAILURES,...errors({not_imap_account:"The account reads mail through a provider's API, not IMAP.",password_required:"A server, login user or password source changes without its password.",password_not_stored:"The account has no stored password and none is sent.",dav_unsupported:"dav is given for an account that signs in with OAuth.",oauth_servers_fixed:"The servers of an account that signs in with OAuth are changed.",password_unsupported:"A password is sent for an account that signs in with OAuth.",account_reauth_required:"The OAuth account's stored grant no longer works, so its servers cannot be checked.",credential_missing:"The OAuth account has no stored credential.",credential_conflict:"The OAuth account's credential kept changing while it was refreshed.",invalid_client:"The provider refuses the OAuth account's client credentials.",token_exchange_failed:"The provider's token endpoint refuses the OAuth account's refresh.",provider_not_configured:"The platform OAuth app the account signed in with is gone.",oauth_app_secret_unreadable:"The client secret of the account's OAuth app cannot be decrypted; replace it."})}).input(AccountIdInput.extend(UpdateImapSettings.shape)).output(Account)},autoconfig={lookup:base.route({method:"GET",path:"/v1/autoconfig",tags:["Accounts"],summary:"Find IMAP, SMTP and DAV settings for an address",description:"dav carries CalDAV and CardDAV context URLs when a source names them; they need the login to resolve to the account's home sets, which creating the account does."}).errors(errors({autodiscovery_failed:"No IMAP settings were found for the address; ask the user for the servers then."})).input(AutoconfigQuery).output(DiscoveredSettings)};var ai={draft:base.route({method:"POST",path:"/v1/ai/draft",tags:["AI"],summary:"Draft email text with AI",description:"Writes an email body from instructions, optionally answering a message or reworking a draft, in plain text or Markdown. Nothing is sent or saved: the text is for a person to edit. Needs the write scope; drafts are held to a per-minute limit (beyond it, rate_limited with Retry-After). A workspace without a subscription (the free tier) may write only as many drafts a month as its included amount (plan_limit_ai), and one that is not its owner's Free workspace and has no plan may write none (plan_required)."}).errors({...planModule("AI"),...errors({plan_limit_ai:"The workspace has no subscription and has written all the AI drafts its free tier includes this month (UTC).",plan_required:PLAN_REQUIRED_CONDITION,spend_cap_reached:spendCapCondition("a draft goes beyond the AI drafts the plan includes")}),...accountState("The account of the message being answered"),...PROVIDER_FAILURES,...errors({ai_disabled:"AI drafting is unavailable.",unknown_account:"context.accountId names an account the tenant does not have; data.ids lists it.",unknown_message:"context.replyToMessageId names a message the account does not have.",mail_disabled:"The account of the message being answered has its mail off.",provider_not_found:"The provider no longer has the message being answered.",provider_forbidden:"The provider refused to read the message being answered.",ai_refused:"The model declines the instructions.",ai_unavailable:"The AI service fails or does not answer in time.",ai_suspended:"The AI service refuses Workwae's requests for now; try again later."})}).input(CreateAiDraft).output(AiDraft)};var auditLog={list:base.route({method:"GET",path:"/v1/audit-log",tags:["Audit log"],summary:"Read the audit log",description:"Every mutating request on /v1 and from the console, newest first. Needs the admin scope. action matches exactly, or as a prefix when it ends with a dot (accounts.). outcome success is a 2xx status."}).errors({...INVALID_CURSOR,...errors({invalid_range:"to is before from."})}).input(ListAuditLogQuery).output(AuditEntryPage)};import*as z27 from"zod";var CALENDAR_ACCOUNT={...planModule("calendar"),...ACCOUNT_NOT_FOUND,...errors({calendar_disabled:"The account's calendar sync is off, or it is an IMAP account without CalDAV settings."})},CALENDAR_PROVIDER={...accountState(),...PROVIDER_FAILURES,...errors({provider_forbidden:"The provider refused the account access (e.g. the grant lacks calendar access).",provider_not_found:"The event or calendar is gone at the provider.",provider_unsupported:"The provider cannot express the request (e.g. a rule Microsoft has no pattern for), or an IMAP account whose CalDAV server does not schedule has no SMTP server to mail the iTIP message from.",provider_rejected:`The provider refused the request, or more than ${MAX_EVENT_ATTENDEES} attendees would be mailed an iTIP message.`})},CALENDAR_WRITE={...CALENDAR_PROVIDER,...errors({provider_conflict:"The event changed at the provider meanwhile.",calendar_read_only:"The account can only read the event's calendar."})},EVENT_TIMES=errors({invalid_time_zone:"A timed start or end names an unknown time zone.",mixed_event_times:"One end is timed and the other all-day.",invalid_range:"end is not after start.",invalid_recurrence:"recurrence has no RRULE or RDATE line, or does not parse as RFC 5545."}),EVENT_NOT_FOUND=errors({calendar_event_not_found:"The event does not exist in the account's synced calendars."}),MEETING_SERVICE=errors({meeting_service_not_connected:"The account has no connection to the service and the tenant no server app it may use (a password IMAP account may not use one).",meeting_service_reauth_required:"The account's connection or the tenant's server app was refused and must be connected or fixed again.",meeting_recurrence_unsupported:"The service cannot repeat a meeting by the event's rule.",meeting_service_rejected:"The service refused the meeting.",meeting_service_unavailable:"The service could not be reached."}),calendars={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/calendars",tags:["Calendar"],summary:"List calendars"}).errors(CALENDAR_ACCOUNT).input(AccountIdInput).output(CalendarList),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/calendars",tags:["Calendar"],summary:"Create a calendar",description:"Microsoft stores the name and, when given, color (#rrggbb); a description or time zone is provider_unsupported (the mailbox zone applies to every calendar). Google stores the name, description, time zone and color. CalDAV stores the name, description and color (Apple calendar-color); a time zone is provider_unsupported.",successStatus:201}).errors({...CALENDAR_ACCOUNT,...CALENDAR_WRITE}).input(CreateCalendarInput).output(Calendar),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/calendars/{calendarId}",tags:["Calendar"],summary:"Rename a calendar or change its color",description:"Same fields as create. The primary calendar can be renamed where the provider allows it. A calendar the account cannot write is calendar_read_only."}).errors({...CALENDAR_ACCOUNT,...errors({calendar_not_found:"The calendar does not exist."}),...CALENDAR_WRITE}).input(CalendarIdInput.extend(UpdateCalendar.shape)).output(Calendar),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/calendars/{calendarId}",tags:["Calendar"],summary:"Delete a calendar",description:"Refused for the primary calendar (calendar_primary). Events in it leave the index with the calendar and are not announced one by one, as when a sync sees the calendar gone. A provider that still holds events deletes them with the calendar.",successStatus:204}).errors({...CALENDAR_ACCOUNT,...errors({calendar_not_found:"The calendar does not exist.",calendar_primary:"The account's primary calendar cannot be deleted."}),...CALENDAR_WRITE}).input(CalendarIdInput).output(z27.void())},calendarEvents={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/calendar-events",tags:["Calendar"],summary:"List events in a time range, by start",description:"Returns singles, occurrences and exceptions overlapping [start, end), not series definitions. Only the sync window (by default 90 days back to 365 days ahead) is indexed."}).errors({...CALENDAR_ACCOUNT,...INVALID_CURSOR,...errors({invalid_range:`end is not after start, or the range spans more than ${MAX_EVENT_RANGE_DAYS} days.`,unknown_calendar:"calendarId names no synced calendar of the account."})}).input(ListCalendarEventsInput).output(CalendarEventPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/calendar-events/{eventId}",tags:["Calendar"],summary:"Get an event with its body",description:"Works for every kind, including a series (its recurrence rule)."}).errors({...CALENDAR_ACCOUNT,...EVENT_NOT_FOUND,...CALENDAR_PROVIDER}).input(CalendarEventIdInput).output(CalendarEventDetail),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/calendar-events",tags:["Calendar"],summary:"Create an event or a recurring series",description:"onlineMeeting asks the provider for a Teams (Microsoft) or Meet (Google) link; onlineMeetingUrl attaches a link from another service (Zoom, Webex, ...), read back as onlineMeetingUrl. On Microsoft that link is the location's link (location defaults to the URL) and a join line is added to a body sent along. onlineMeetingService has Workwae create a Zoom or Webex meeting (recurring for a series) hosted by the account's own grant at the service, else by the tenant's server app as the service user with the mailbox's address, and attaches its join link the same way. The server app hosts only accounts whose address the provider verified (Gmail, Microsoft, IMAP signed in with OAuth): a password IMAP account's address is whatever was typed, so it needs its own grant. The meeting is created only once the account is active, and deleted again if anything fails before its link is saved. On an IMAP account whose CalDAV server does not schedule, the invitation is mailed from the account's SMTP server when notify is true and there are attendees; a missing SMTP server is refused before anything is created, and a send that fails after the event is created is logged and the event stands.",successStatus:201}).errors({...CALENDAR_ACCOUNT,...CALENDAR_WRITE,...EVENT_TIMES,...MEETING_SERVICE,...errors({plan_module_unavailable:"The tenant's plan does not include calendar, or onlineMeetingService is given and it does not include meetings.",calendar_not_found:"calendarId names no synced calendar of the account.",calendar_event_not_found:"The provider no longer has the event when it is read back.",multiple_meeting_options:"More than one of onlineMeeting, onlineMeetingUrl and onlineMeetingService is given.",meeting_host_missing:"The tenant's server app has no user with the mailbox's address."})}).input(CreateCalendarEventInput).output(CalendarEventDetail),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/calendar-events/{eventId}",tags:["Calendar"],summary:"Change an event, one occurrence, or a whole series",description:"Addressing an occurrence changes only that instance; addressing the series changes every occurrence. onlineMeetingUrl replaces a link from another service (as on create); null removes it (on Google, whatever conference the event has; on Microsoft, the location's link, leaving a Teams meeting alone). For an event whose Zoom or Webex meeting Workwae created, a new subject, start, end or recurrence moves that meeting too, and an onlineMeetingUrl other than its own join link deletes it (sending that link back replaces nothing); changing one occurrence leaves the meeting's series as it is. A missing or refused meeting connection is reported before anything changes; a service failure after the event changed is reported with the event changed (repeat the request). On an IMAP account whose CalDAV server does not schedule, an update of a meeting the account organizes is mailed from its SMTP server when it changes start, end, recurrence, location or attendees, to the attendees Workwae has indexed for it and those the request names; a missing SMTP server or too many attendees is refused before anything changes, and a send that fails after the change is logged and the change stands."}).errors({...CALENDAR_ACCOUNT,...EVENT_NOT_FOUND,...CALENDAR_WRITE,...EVENT_TIMES,...MEETING_SERVICE,...errors({recurrence_on_instance:"recurrence is given for an occurrence or exception.",meeting_deleted:"The event's meeting is gone at the service when it is moved."})}).input(CalendarEventIdInput.extend(UpdateCalendarEvent.shape)).output(CalendarEventDetail),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/calendar-events/{eventId}",tags:["Calendar"],summary:"Delete an event, one occurrence, or a whole series",description:"Cancels the meeting for its attendees when the account organizes it. A Zoom or Webex meeting Workwae created for the event (a series, not one occurrence) is deleted after the provider event: a provider refusal leaves both, and once the event is gone a meeting the service cannot delete (connection gone or refused, service unreachable) is logged and stays at the service while the request still succeeds. On an IMAP account whose CalDAV server does not schedule, the cancellation is mailed from the account's SMTP server to the attendees Workwae has indexed; a missing SMTP server or too many attendees is refused before anything is deleted, and a send that fails after the deletion is logged.",successStatus:204,inputStructure:"detailed"}).errors({...CALENDAR_ACCOUNT,...EVENT_NOT_FOUND,...CALENDAR_WRITE}).input(DeleteCalendarEventInput).output(z27.void()),respond:base.route({method:"POST",path:"/v1/accounts/{accountId}/calendar-events/{eventId}/respond",tags:["Calendar"],summary:"Accept, tentatively accept or decline an invitation",description:"Addressing an occurrence answers that instance; the series answers all of them. On an IMAP account whose CalDAV server does not schedule, the answer is mailed to the organizer from the account's SMTP server; a missing SMTP server is refused before anything changes, and a send that fails after the answer is recorded is logged."}).errors({...CALENDAR_ACCOUNT,...EVENT_NOT_FOUND,...CALENDAR_PROVIDER,...errors({provider_conflict:"The event changed at the provider meanwhile.",organizer_cannot_respond:"The account organizes the event, so there is no invitation to answer."})}).input(CalendarEventIdInput.extend(RespondToCalendarEventRequest.shape)).output(CalendarEvent)},AVAILABILITY_INPUT=errors({invalid_range:`end is not after start, or the range spans more than ${MAX_AVAILABILITY_DAYS} days.`,invalid_time_zone:"timeZone is unknown.",invalid_working_hours:"workingHours does not end after it starts."}),availability={find:base.route({method:"POST",path:"/v1/accounts/{accountId}/availability/find",tags:["Calendar"],summary:"Find busy times and common free slots",description:"A read sent as a POST, the question not fitting a URL. One provider request covers every attendee (Microsoft getSchedule, Google freeBusy.query); the answer is cached briefly per account, attendees and range. An attendee whose free/busy cannot be read (unknown address, calendar not shared) is listed in unresolved and complete is false; by default such an attendee does not constrain the suggestions, and with requireAll no suggestions are made (withheldReason unresolved_attendees)."}).errors({...CALENDAR_ACCOUNT,...CALENDAR_PROVIDER,...AVAILABILITY_INPUT}).input(FindAvailabilityInput).output(Availability),findForAccounts:base.route({method:"POST",path:"/v1/availability/find",tags:["Calendar"],summary:"Find free slots across several accounts",description:`Reads up to ${MAX_AVAILABILITY_ACCOUNTS} of the tenant's accounts, each through its own calendar, and suggests slots where all of them are free (mode all) or at least one is (mode any, for round-robin), with the accounts free for each slot. Other attendees are looked up through the first account and must all be free in either mode. An account that cannot be read (calendar sync off, paused, needing a new sign-in, a provider failure) is listed in unresolved with its error: in mode all it does not constrain the suggestions, in mode any it is never offered as free; requireAll withholds every suggestion instead (withheldReason unresolved_attendees). A read sent as a POST, like the single-account question, and not open to credentials limited to specific accounts.`}).errors({...planModule("calendar"),...AVAILABILITY_INPUT,...errors({unknown_account:"accountIds names an account the tenant does not have (data lists them)."})}).input(FindAccountsAvailabilityInput).output(AccountsAvailability)};var MAIL_CONNECT_MODULES="mail (options.mailAccess imap) or shared mailboxes (options.sharedMailboxesEnabled: true)",CONNECT_REQUEST=errors({redirect_uri_not_allowed:"redirectUri is not on the tenant allowlist (settings.redirectUris).",billing_past_due:"accountId is not given and the tenant's payment is past due beyond the grace period.",unknown_account:"accountId names no live account of the tenant; data.ids names it.",org_connection_account:"accountId names an account that authenticates through an organization connection (test the connection to restore it).",shared_mailbox_account:"accountId names a shared mailbox (reconnect the account that opened it)."}),CONNECT_SESSION_NOT_FOUND=errors({connect_session_not_found:"No session has this id (or token)."}),HOSTED_IMAP_SESSION={...CONNECT_SESSION_NOT_FOUND,...errors({session_used:"The session already completed or failed.",session_expired:"The session is past expiresAt.",redirect_uri_not_allowed:"The session's return address is no longer on the tenant allowlist.",provider_not_allowed:"The session does not offer the IMAP form."})},connectSessions={create:base.route({method:"POST",path:"/v1/connect-sessions",tags:["Connect"],summary:"Create a hosted connect session",description:"Returns a link to the hosted connect page. Its OAuth sign-ins pass a Continue page naming the tenant and provider, on the API origin or, for a tenant OAuth app that uses the workspace's connect domain (useConnectDomain), on that domain, and must finish in the browser that pressed Continue (an HttpOnly cookie on that origin); a callback in any other browser renders an error page and connects nothing. A sign-in through an app whose connect domain is not active returns to redirectUri with error=connect_domain_inactive. Without redirectUri the owner ends on Workwae's own confirmation page, which needs no allowlist entry; follow the outcome with GET /v1/connect-sessions/{sessionId} or the account.connected event. A failed sign-in returns with status=error and error set to a ConnectErrorCode.",successStatus:201}).errors({...CONNECT_REQUEST,...bodyStorage("options.bodyStorage"),...planModule(MAIL_CONNECT_MODULES),...errors({unknown_oauth_app:"oauthAppIds names no OAuth app of the tenant.",oauth_app_mismatch:"An OAuth app in oauthAppIds is for the other provider.",account_mismatch:"providers leaves out the sign-in the account reconnects with.",google_imap_requires_own_app:"providers includes google with options.mailAccess imap (given, or kept by the account being reconnected) and no Google app (oauthAppIds, or the reconnected account's own): Google over IMAP needs the tenant's own Google OAuth app.",mcp_key_limit:"mcp is given with accountId, and that account already has as many live MCP keys as it may."})}).input(CreateConnectSession).output(SessionLink),get:base.route({method:"GET",path:"/v1/connect-sessions/{sessionId}",tags:["Connect"],summary:"Read a hosted connect session's outcome"}).errors(CONNECT_SESSION_NOT_FOUND).input(ConnectSessionIdInput).output(ConnectSession)},oauthAuthorizations={create:base.route({method:"POST",path:"/v1/oauth-authorizations",tags:["Connect"],summary:"Start a headless OAuth connect",description:"authorizeUrl is a page naming the tenant and the provider, on the API origin or, for a tenant OAuth app that uses the workspace's connect domain (useConnectDomain), on that domain; only its Continue button (a form POST, accepted only from that page's origin or the console's) leads on to the provider. Continuing works once and binds the sign-in to that browser with an HttpOnly cookie scoped to the callback; a callback arriving in any other browser renders an error page and connects nothing, so send the user's own browser to it. A failed sign-in returns with status=error and error set to a ConnectErrorCode."}).errors({...CONNECT_REQUEST,...bodyStorage("options.bodyStorage"),...planModule(MAIL_CONNECT_MODULES),...OAUTH_CLIENT,...errors({connect_domain_inactive:"The OAuth app (oauthAppId, or the reconnected account's own) uses the workspace's connect domain and it is not active.",connect_grants_nothing:"The plan's modules and the settings (the tenant's, the account's on a reconnect, and options) leave no mail, calendar or contacts for the grant to cover.",account_mismatch:"The account given as accountId signs in with another provider, or with a password.",google_imap_requires_own_app:"provider google with options.mailAccess imap (given, or kept by the account being reconnected) and no Google app (oauthAppId, or the reconnected account's own): Google over IMAP needs the tenant's own Google OAuth app."})}).input(StartOAuthRequest).output(OAuthAuthorization)},HOSTED_TAGS=["Hosted pages"],hostedConnectSessions={get:base.route({method:"GET",path:"/v1/hosted/connect-sessions/{token}",tags:HOSTED_TAGS,summary:"Read a hosted connect session",spec:publicOperation}).errors(CONNECT_SESSION_NOT_FOUND).input(SessionTokenInput).output(ConnectSessionPublic),createImapAccount:base.route({method:"POST",path:"/v1/hosted/connect-sessions/{token}/imap-accounts",tags:HOSTED_TAGS,summary:"Connect an IMAP account from the hosted page",description:"When the tenant's plan allows no more accounts, or its payment is past due beyond the grace period, the session fails and redirectUrl carries status=error&error=plan_limit_accounts (or billing_past_due), as the OAuth callback does. Giving dav switches settings.calendarEnabled and contactsEnabled on for the halves found unless the session's options name them, as POST /v1/imap-accounts does. A reconnect session replaces the password only: the account keeps its stored servers and DAV settings, and any sent here are ignored. Its DAV login is checked again: with the new password when DAV uses the IMAP password, otherwise with dav.password when given (which then replaces the stored DAV password) or the stored one.",spec:publicOperation}).errors({...HOSTED_IMAP_SESSION,...MAIL_SETTINGS_CHECK,...AUTODISCOVERY_FAILED,...DAV_SETTINGS_CHECK,...PROVIDER_FAILURES,...errors({account_exists:"The tenant already has a live IMAP account with this address and the session was not created to reconnect it (accountId).",account_not_found:"The account a reconnect session was created for was deleted.",account_mismatch:"A reconnect session's account has another address, or is not an IMAP account.",password_required:"A reconnect session's DAV has a password of its own, none is stored and dav.password is left out."})}).input(SessionTokenInput.extend(ImapAccountInput.shape)).output(HostedImapResult),autoconfig:base.route({method:"GET",path:"/v1/hosted/connect-sessions/{token}/autoconfig",tags:HOSTED_TAGS,summary:"Find IMAP and SMTP settings from the hosted page",spec:publicOperation}).errors({...HOSTED_IMAP_SESSION,...errors({autodiscovery_failed:"No IMAP settings were found for the address."})}).input(SessionTokenInput.extend(AutoconfigQuery.shape)).output(DiscoveredSettings)};import*as z28 from"zod";var UNAVAILABLE=errors({connect_domain_unavailable:"The deployment has no Cloudflare for SaaS configured for custom domains."}),NOT_FOUND=errors({connect_domain_not_found:"The workspace has no connect domain."}),EDGE=errors({connect_domain_edge_unavailable:"The edge provider (Cloudflare) refused or could not be reached."}),IN_USE=errors({connect_domain_in_use:"An OAuth app, meeting app or organization connection of the workspace uses the connect domain (useConnectDomain): turn that off first."}),connectDomain={get:base.route({method:"GET",path:"/v1/settings/connect-domain",tags:["Settings"],summary:"Get the workspace's connect domain",description:"The workspace's own host for the browser steps of connecting an account through its own provider apps (the redirect URI to register at Google, Microsoft, Zoom or Webex), with the DNS records to publish and where setting it up stands. Requires the admin scope."}).errors({...UNAVAILABLE,...NOT_FOUND}).output(ConnectDomain),set:base.route({method:"PUT",path:"/v1/settings/connect-domain",tags:["Settings"],summary:"Set the workspace's connect domain",description:"Registers the hostname with the edge and answers the DNS records to publish: a CNAME to the deployment (records), any TXT records the edge asks for (records), and the ownership TXT record that is random to this claim (ownership). The domain is pending until the ownership record is published and the edge reports the hostname and its certificate active (checked every few minutes, or on POST /v1/settings/connect-domain/refresh); only then can an OAuth app, meeting app or organization connection be set to use it (useConnectDomain). The redirect URI such an app registers at its provider is then `https://<hostname>/connect/callback/<provider>`. Replaces the workspace's current connect domain once the new hostname is registered, so a refusal leaves the current one serving; the same hostname again changes nothing, unless it failed. Not available for a different hostname while an app uses the connect domain. Requires the admin scope."}).errors({...UNAVAILABLE,...EDGE,...IN_USE,...errors({connect_domain_not_allowed:"The hostname is one of the deployment's own, or a name the edge cannot take.",connect_domain_taken:"A workspace uses the hostname, as its connect or tracking domain."})}).input(SetConnectDomain).output(ConnectDomain),delete:base.route({method:"DELETE",path:"/v1/settings/connect-domain",tags:["Settings"],summary:"Remove the workspace's connect domain",description:"Removes the hostname from the edge and the workspace. Refused while an OAuth app, meeting app or organization connection uses it. Requires the admin scope.",successStatus:204}).errors({...UNAVAILABLE,...NOT_FOUND,...EDGE,...IN_USE}).output(z28.void()),refresh:base.route({method:"POST",path:"/v1/settings/connect-domain/refresh",tags:["Settings"],summary:"Check the workspace's connect domain now",description:"Reads the hostname's state from the edge now instead of waiting for the next check, and answers the updated domain. Requires the admin scope."}).errors({...UNAVAILABLE,...NOT_FOUND,...EDGE}).output(ConnectDomain)};import*as z29 from"zod";var CONTACTS_ACCOUNT={...planModule("contacts"),...ACCOUNT_NOT_FOUND,...errors({contacts_disabled:"The account's contacts sync is off, or it is an IMAP account without CardDAV settings."})},CONTACT_NOT_FOUND=errors({contact_not_found:"The contact does not exist."}),CONTACTS_PROVIDER={...accountState(),...PROVIDER_FAILURES,...errors({provider_forbidden:"The provider refused the account access (e.g. the grant lacks contacts access).",provider_not_found:"The contact is gone at the provider (the index drops it).",provider_conflict:"The contact changed at the provider meanwhile.",provider_rejected:"The provider refused the request."})},CONTACT_WRITE={...CONTACTS_PROVIDER,...errors({contact_unidentifiable:"The contact would have no name, organization, email address or phone number.",provider_unsupported:'The provider cannot store what was given (a folder on Google, or on Microsoft 365 more than 3 email addresses, more than one mobile number or an "other" number).'})},contactFolders={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/contact-folders",tags:["Contacts"],summary:"List contact folders",description:"Microsoft 365 contact folders, the default folder first; empty for Google accounts."}).errors(CONTACTS_ACCOUNT).input(AccountIdInput).output(ContactFolderList)},contacts={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/contacts",tags:["Contacts"],summary:"List or search contacts, by name",description:"Reads the index. q matches part of any name, the organization or any email address, case-insensitively."}).errors({...CONTACTS_ACCOUNT,...INVALID_CURSOR,...errors({unknown_contact_folder:"folderId names no contact folder of the account."})}).input(AccountIdInput.extend(ListContactsQuery.shape)).output(ContactPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/contacts/{contactId}",tags:["Contacts"],summary:"Get a contact",description:"Reads the index."}).errors({...CONTACTS_ACCOUNT,...CONTACT_NOT_FOUND}).input(ContactIdInput).output(Contact),photo:base.route({method:"GET",path:"/v1/accounts/{accountId}/contacts/{contactId}/photo",tags:["Contacts"],summary:"Download a contact's photo",description:"Fetched from the provider on first request and cached until the photo changes.",spec:fileResponse({mediaType:"image/*",description:"The photo as the provider stores it (image/jpeg, image/png, ...)",disposition:"inline"})}).errors({...CONTACTS_ACCOUNT,...CONTACT_NOT_FOUND,...CONTACTS_PROVIDER,...errors({photo_not_found:"The contact has no photo."})}).input(ContactIdInput).output(z29.file()),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/contacts",tags:["Contacts"],summary:"Create a contact",description:"Written to the provider, then to the index, so it is listed at once.",successStatus:201}).errors({...CONTACTS_ACCOUNT,...CONTACT_WRITE,...errors({unknown_contact_folder:"folderId names no contact folder of the account.",contact_not_found:"The provider's answer could not be read back into the index."})}).input(CreateContactInput).output(Contact),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/contacts/{contactId}",tags:["Contacts"],summary:"Change a contact",description:"Fields present change (null clears); emails and phones replace the whole list."}).errors({...CONTACTS_ACCOUNT,...CONTACT_NOT_FOUND,...CONTACT_WRITE}).input(ContactIdInput.extend(UpdateContact.shape)).output(Contact),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/contacts/{contactId}",tags:["Contacts"],summary:"Delete a contact",successStatus:204}).errors({...CONTACTS_ACCOUNT,...CONTACT_NOT_FOUND,...CONTACTS_PROVIDER}).input(ContactIdInput).output(z29.void())};var emailValidations={create:base.route({method:"POST",path:"/v1/email-validations",tags:["Email validation"],summary:"Validate email addresses",description:"Checks whether each address (up to 10) can receive mail, without sending any: syntax, the domain's mail servers and an SMTP conversation with the recipient's server. Takes seconds per address. An address checked within the deployment's cache period (7 days by default) gets that result back, cached, unless refresh is set or it was unknown. The addresses checked again count toward the plan's emailValidationsPerDay; reused results do not, and a request that does not fit is refused whole. On a billed plan with included validation credits, each address checked again also spends one credit (included credits first, then purchased ones; reused results are free), and a request the balance cannot cover is refused whole with 402 validation_credits_exhausted. A request that fails costs no credits. With suppressInvalid, addresses found invalid go on the global suppression list. Every result is kept in the history (GET /v1/email-validations). Needs the write scope; requests are held to a per-minute limit (beyond it, rate_limited with Retry-After)."}).errors(errors({email_validation_disabled:"Email validation is unavailable.",email_validation_unavailable:"The validation service fails or does not answer in time; no address is suppressed.",email_validation_suspended:"The validation service refuses Workwae's requests for now; try again later.",plan_limit_validations:"The addresses to check again exceed what the plan allows today (UTC); nothing was checked.",plan_required:PLAN_REQUIRED_CONDITION,validation_credits_exhausted:"The tenant has fewer email validation credits than the addresses to check again; data.balance and data.needed say how many; nothing was checked."})).input(CreateEmailValidation).output(EmailValidation),list:base.route({method:"GET",path:"/v1/email-validations",tags:["Email validation"],summary:"List earlier validation results",description:"One item per address a validation request answered, newest first, kept for the deployment's retention period (90 days by default). address keeps those containing it, reachability those with that verdict. Readable whether or not the deployment validates."}).errors(INVALID_CURSOR).input(ListEmailValidationsQuery).output(EmailValidationRecordPage)};var changes={list:base.route({method:"GET",path:"/v1/changes",tags:["Changes"],summary:"Read the change feed",description:"Events oldest first. Pass nextCursor back as cursor to continue; it is never null, since the feed has no end, only a present: an empty page means caught up, so poll again from the same cursor later. An API key bound to accounts reads only their events, without the tenant-level ones (accountId null)."}).errors(INVALID_CURSOR).input(ListChangesQuery).output(EventFeedPage)};import*as z30 from"zod";var EXPORT_NOT_FOUND=errors({export_not_found:"The export does not exist."}),exports={create:base.route({method:"POST",path:"/v1/exports",tags:["Exports"],summary:"Export the tenant or one account",description:"Builds a gzip-compressed tar in the background: accounts without secrets, mailboxes, message metadata (JSON lines), stored message bodies as .eml, calendars and events as JSON and .ics, and for a tenant export its webhooks (without signing secrets), templates and settings. Follow it with GET /v1/exports/{exportId}, or the export.completed and export.failed events. Requires the admin scope.",successStatus:202}).errors(errors({storage_unavailable:"No object store is configured.",unknown_account:"accountId names an account the tenant does not have; data.ids lists it.",export_in_progress:"An export of the same scope (the tenant, or the same account) is queued or running."})).input(CreateDataExport).output(DataExport),list:base.route({method:"GET",path:"/v1/exports",tags:["Exports"],summary:"List exports, newest first",description:"Requires the admin scope."}).errors(INVALID_CURSOR).input(ListDataExportsQuery).output(DataExportPage),get:base.route({method:"GET",path:"/v1/exports/{exportId}",tags:["Exports"],summary:"Get an export",description:"Requires the admin scope."}).errors(EXPORT_NOT_FOUND).input(DataExportIdInput).output(DataExport),download:base.route({method:"GET",path:"/v1/exports/{exportId}/download",tags:["Exports"],summary:"Download an export (application/gzip)",description:"Streams the decrypted .tar.gz; compare it with the export's sha256. Requires the admin scope.",spec:fileResponse({mediaType:"application/gzip",description:"The export as a gzip-compressed tar",disposition:"attachment",headers:{"x-export-sha256":{description:"sha256 hex of the body, the export's sha256",schema:{type:"string"}}}})}).errors({...EXPORT_NOT_FOUND,...errors({export_not_ready:"The export is queued or running.",export_failed:"The export failed.",export_expired:"The export's file was deleted.",storage_unavailable:"No object store is configured.",export_corrupt:"A stored part of the export fails its integrity check."})}).input(DataExportIdInput).output(StreamedFile),delete:base.route({method:"DELETE",path:"/v1/exports/{exportId}",tags:["Exports"],summary:"Delete an export and its file",description:"Requires the admin scope.",successStatus:204}).errors({...EXPORT_NOT_FOUND,...errors({export_running:"The export is being built."})}).input(DataExportIdInput).output(z30.void())};import*as z31 from"zod";var IDENTITY_NOT_FOUND=errors({identity_not_found:"The identity does not exist."}),identities={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/identities",tags:["Identities"],summary:"List the addresses the account may send as",description:"The account's own address, the provider's (Gmail send-as addresses with their Gmail signatures in one users.settings.sendAs request; Microsoft proxy addresses from /me) and custom ones. The provider's list is read when the cached one is a day old, or with refresh=true at most once a minute; a paused or disconnected account serves the cached list."}).errors({...MAIL_ACCOUNT,...errors({account_reauth_required:"Reading the provider's list found that the account must be reconnected."}),...PROVIDER_FAILURES}).input(AccountIdInput.extend(ListIdentitiesQuery.shape)).output(IdentityList),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/identities",tags:["Identities"],summary:"Add a custom identity",description:"For IMAP aliases, and Microsoft mailboxes the account has Send As permission on (Graph cannot list those). Sending as it is checked by the provider when the message goes out.",successStatus:201}).errors({...MAIL_ACCOUNT,...errors({identities_managed_by_provider:"The account is a Gmail account (add send-as addresses in Gmail).",identity_exists:"The address is already an identity of the account."})}).input(AccountIdInput.extend(CreateIdentity.shape)).output(Identity),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/identities/{identityId}",tags:["Identities"],summary:"Change an identity's signature or name",description:"signature: null removes ours (Gmail's own then applies again). Only custom identities take a name; the others carry the provider's."}).errors({...MAIL_ACCOUNT,...IDENTITY_NOT_FOUND,...errors({identity_not_custom:"A name is given for a primary or provider identity."})}).input(IdentityIdInput.extend(UpdateIdentity.shape)).output(Identity),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/identities/{identityId}",tags:["Identities"],summary:"Remove a custom identity",successStatus:204}).errors({...MAIL_ACCOUNT,...IDENTITY_NOT_FOUND,...errors({identity_not_custom:"The identity is the primary or a provider identity (those follow the provider)."})}).input(IdentityIdInput).output(z31.void())},mailSettings={get:base.route({method:"GET",path:"/v1/accounts/{accountId}/mail-settings",tags:["Identities"],summary:"Get the account's mail settings"}).errors(MAIL_ACCOUNT).input(AccountIdInput).output(MailSettings),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/mail-settings",tags:["Identities"],summary:"Update the account's mail settings"}).errors(MAIL_ACCOUNT).input(AccountIdInput.extend(UpdateMailSettings.shape)).output(MailSettings)};import*as z32 from"zod";var UNKNOWN_MAILBOX=errors({unknown_mailbox:"A mailbox id names no live mailbox of the account; data.ids lists them."}),PROVIDER_CHANGE={...PROVIDER_FAILURES,...errors({provider_not_found:"The provider no longer has the item.",provider_forbidden:"The provider refused the change for lack of permission.",provider_rejected:"The provider rejected the change as invalid.",provider_unsupported:"The provider does not support the change.",provider_conflict:"The provider refused the change because of the item's state."})},MESSAGE_READ={...MAIL_ACCOUNT,...errors({message_not_found:"The message does not exist."}),...accountState("The provider is needed and the account"),...PROVIDER_FAILURES,...errors({provider_not_found:"The provider no longer has the message or the attachment.",provider_forbidden:"The provider refused to serve the message."})},LIST_BODIES={...accountState("With include=body, the account"),...PROVIDER_FAILURES,...errors({provider_not_found:"With include=body, the provider no longer has one of the messages.",provider_forbidden:"With include=body, the provider refused to serve one of the messages."})},GROUP_MAILBOX_REFUSAL="A Google Group account has no mailboxes of its own to change.",mailboxes={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/mailboxes",tags:["Mail"],summary:"List mailboxes (folders or labels)"}).errors(MAIL_ACCOUNT).input(AccountIdInput).output(MailboxList),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/mailboxes",tags:["Mail"],summary:"Create a mailbox (folder or label)",description:"Microsoft and IMAP create a folder; Gmail creates a label (nested with '/' in the label name when parentId is set). A folder that is not empty can be deleted later: Microsoft deletes the folder and the messages in it, Gmail removes the label and leaves the messages, IMAP deletes the mailbox and its messages and refuses a mailbox that still has child mailboxes (provider_rejected).",successStatus:201}).errors({...MAIL_ACCOUNT,...UNKNOWN_MAILBOX,...errors({group_unsupported:GROUP_MAILBOX_REFUSAL}),...accountState(),...PROVIDER_CHANGE}).input(CreateMailboxInput).output(Mailbox),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/mailboxes/{mailboxId}",tags:["Mail"],summary:"Rename or move a mailbox",description:"Refused for system and special-use mailboxes (Inbox, Sent, Drafts, Trash, Junk, Archive, Gmail system labels) with mailbox_protected. parentId null moves it to the top level. On Gmail the label's name keeps '/' between a parent label and this one."}).errors({...MAIL_ACCOUNT,...UNKNOWN_MAILBOX,...errors({mailbox_protected:"A system or special-use mailbox cannot be renamed or moved.",mailbox_parent:"parentId is this mailbox or a mailbox nested under it.",group_unsupported:GROUP_MAILBOX_REFUSAL}),...accountState(),...PROVIDER_CHANGE}).input(MailboxIdInput.extend(UpdateMailbox.shape)).output(Mailbox),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/mailboxes/{mailboxId}",tags:["Mail"],summary:"Delete a mailbox",description:"Refused for system and special-use mailboxes (mailbox_protected). Microsoft deletes the folder and the messages in it (child folders are refused, provider_rejected). Gmail deletes the label and takes it off its messages, which stay. IMAP deletes the mailbox and its messages; a mailbox that still has children is refused by the server (provider_rejected). The index drops the mailbox now; messages it held leave on the next sync, the same way a deletion made elsewhere does.",successStatus:204}).errors({...MAIL_ACCOUNT,...errors({unknown_mailbox:"mailboxId names no live mailbox of the account.",mailbox_protected:"A system or special-use mailbox cannot be deleted.",group_unsupported:GROUP_MAILBOX_REFUSAL}),...accountState(),...PROVIDER_CHANGE}).input(MailboxIdInput).output(z32.void())},messages={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/messages",tags:["Mail"],summary:"List messages, newest first",description:"Covers the account's sync window. Without mailboxId, lists every mailbox. importance keeps the messages with that priority; messages indexed before priorities were read (importance null) match none. from and to match part of an address or display name, case-insensitive (to looks at To, Cc and Bcc); after and before bound receivedAt. outboxId and internetMessageId match exactly (internetMessageId as one <id>, angle brackets included) and combine with every other filter. include=body answers MessagePage, with each item the full Message that messages.get returns (add headers for its header fields: include=body,headers), instead of MessageSummaryPage; the page holds 25 messages at most (default 25), a larger limit is refused. Bodies come from stored content when the account stores bodies and from the provider otherwise, a few messages at a time; a message the provider cannot serve fails the whole page with the error messages.get would answer for it."}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR,...UNKNOWN_MAILBOX,...LIST_BODIES,...errors({invalid_range:"before is not after after."})}).input(withListIncludeRules(withDateRange(AccountIdInput.extend(ListMessagesQuery.shape),{from:"after",to:"before"}))).output(z32.union([MessagePage,MessageSummaryPage])),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/messages/{messageId}",tags:["Mail"],summary:"Get a message with its body",description:`With bodyStorage cache or full the body comes from stored content (cache fetches and stores the raw message on a miss), and attachment ids are MIME part numbers ("1.2"); otherwise it is read from the provider. Drafts are always read from the provider. inReplyTo and references come from the message's In-Reply-To and References headers; include=headers adds every top-level header field (headers), which is left out otherwise.`}).errors(MESSAGE_READ).input(GetMessageInput).output(Message),raw:base.route({method:"GET",path:"/v1/accounts/{accountId}/messages/{messageId}/raw",tags:["Mail"],summary:"Download the message as RFC 5322 (message/rfc822)",description:"Served from stored content when the account stores bodies.",spec:fileResponse({mediaType:"message/rfc822",description:"The message as message/rfc822",disposition:"attachment"})}).errors(MESSAGE_READ).input(MessageIdInput).output(StreamedFile),attachment:base.route({method:"GET",path:"/v1/accounts/{accountId}/messages/{messageId}/attachments/{attachmentId}",tags:["Mail"],summary:"Download an attachment",description:"Served from stored content when the account stores bodies; an id the stored message does not have is asked of the provider, which answers provider_not_found when it has no such attachment either.",spec:fileResponse({mediaType:"*/*",description:"The attachment, with the Content-Type it was sent with",disposition:"attachment"})}).errors(MESSAGE_READ).input(AttachmentIdInput).output(StreamedFile),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/messages/{messageId}",tags:["Mail"],summary:"Change flags, move, or change labels",description:"mailboxId moves the message (Microsoft folders, or Gmail: replaces the message's folder-like labels). addMailboxIds/removeMailboxIds apply Gmail labels."}).errors({...MAIL_ACCOUNT,...errors({message_not_found:"The message does not exist.",mailbox_not_label:"addMailboxIds or removeMailboxIds names a mailbox that is not a label."}),...UNKNOWN_MAILBOX,...accountState(),...PROVIDER_CHANGE}).input(MessageIdInput.extend(UpdateMessage.shape)).output(MessageSummary),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/messages/{messageId}",tags:["Mail"],summary:"Delete a message",description:"Moves it to Trash; ?permanent=true deletes it outright.",successStatus:204,inputStructure:"detailed"}).errors({...MAIL_ACCOUNT,...errors({message_not_found:"The message does not exist."}),...accountState(),...PROVIDER_CHANGE}).input(DeleteMessageInput).output(z32.void())},search={messages:base.route({method:"GET",path:"/v1/accounts/{accountId}/search/messages",tags:["Mail"],summary:"Search messages",description:['source=index searches the index with full-text search (q in web search syntax: words, "phrases", OR, -word) over subject, sender and recipient names and addresses, the snippet, and the body text of stored messages (bodyStorage cache or full), best matches first.',"source=provider passes q to the provider unchanged (Gmail search syntax, Microsoft KQL, IMAP SEARCH TEXT), newest first; results the index holds carry their id, others id null.","source=auto (default) asks the provider when the index cannot cover the request: `after` lies before the sync window, or the index found nothing while it does not hold everything (the account has a sync window, or bodyStorage is not full). Otherwise, and whenever the provider cannot be asked (inactive account, rate limit, provider error), the index answers.","from, to, unread, flagged and hasAttachments narrow either source's results as on messages.list; a provider page they narrow can hold fewer than limit results while nextCursor goes on.","The response names the source that answered; nextCursor stays with it.","A provider search of a Gmail account counts against the account's Gmail searches per minute; past them it answers rate_limited with Retry-After."].join(" ")}).errors({...MAIL_ACCOUNT,...errors({invalid_range:"before is not after after."}),...errors({invalid_cursor:"The cursor is invalid, was issued for another account, or is a provider cursor older than an hour."}),...UNKNOWN_MAILBOX,...accountState("With source=provider, the account"),...PROVIDER_FAILURES,...errors({provider_forbidden:"With source=provider, the provider refused the search.",provider_rejected:"With source=provider, the provider rejected q.",provider_unsupported:"With source=provider, the provider cannot run the search."})}).input(withDateRange(AccountIdInput.extend(SearchMessagesQuery.shape),{from:"after",to:"before"})).output(SearchPage)};import*as z33 from"zod";var TAGS=["MCP keys"],mcpKeys={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/mcp-keys",tags:TAGS,summary:"Mint an MCP key for an account's user",description:"The key is returned once; it is not retrievable later. It works only at the MCP server (mcpUrl, sent as `Authorization: Bearer <key>`), only for this account and with these scopes; every other route answers it with mcp_only_key. MCP clients that send a header take it (Claude Code, Cursor, Codex and the like); Claude.ai and ChatGPT custom connectors sign in with OAuth instead. Needs the admin scope and a key that is not bound to accounts. Revoked with the account when it is deleted.",successStatus:201}).errors({...ACCOUNT_NOT_FOUND,...errors({mcp_key_limit:`The account already has ${MAX_MCP_KEYS_PER_ACCOUNT} live MCP keys; revoke one first.`})}).input(AccountIdInput.extend(CreateMcpKey.shape)).output(CreatedMcpKey),list:base.route({method:"GET",path:"/v1/accounts/{accountId}/mcp-keys",tags:TAGS,summary:"List an account's MCP keys",description:"Newest first, without their secrets; revoked keys are left out."}).errors(ACCOUNT_NOT_FOUND).input(AccountIdInput).output(McpKeyList),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/mcp-keys/{keyId}",tags:TAGS,summary:"Revoke an MCP key",description:"The MCP server refuses the key from its next request on.",successStatus:204}).errors({...ACCOUNT_NOT_FOUND,...errors({mcp_key_not_found:"The account has no live MCP key with this id."})}).input(McpKeyIdInput).output(z33.void())};import*as z34 from"zod";var APP_TAGS=["Meeting apps"],CONNECTION_TAGS=["Meeting connections"],MEETINGS=planModule("Zoom and Webex meetings"),APP_NOT_FOUND=errors({meeting_app_not_found:"The app does not exist."}),CONNECT_DOMAIN=errors({connect_domain_inactive:"useConnectDomain is true and the workspace has no active connect domain (settings.connectDomain).",connect_domain_unsupported:"useConnectDomain is true for a server app (zoom_s2s, webex_service_app), which has no sign-in to move."}),meetingApps={list:base.route({method:"GET",path:"/v1/meeting-apps",tags:APP_TAGS,summary:"List meeting apps"}).errors(MEETINGS).output(MeetingAppList),create:base.route({method:"POST",path:"/v1/meeting-apps",tags:APP_TAGS,summary:"Register a meeting app",description:"A sign-in app (zoom_oauth, webex_oauth) is named by meeting connect sessions in place of the platform's; register `redirectUri` at the service. It is on the API host, or on the workspace's connect domain when `useConnectDomain` is set. A server app (zoom_s2s, webex_service_app) starts unverified; test it.",successStatus:201}).errors({...MEETINGS,...CONNECT_DOMAIN,...errors({meeting_app_exists:"The tenant already has a server app for the service."})}).input(CreateMeetingApp).output(MeetingApp),update:base.route({method:"PATCH",path:"/v1/meeting-apps/{appId}",tags:APP_TAGS,summary:"Change a meeting app: its connect domain",description:"useConnectDomain moves a sign-in app's sign-ins to the workspace's connect domain, or back to the API host; register the new `redirectUri` at the service first. A sign-in already started finishes on the host it began on. If the connect domain stops being active while an app uses it, the app's sign-ins fail with connect_domain_inactive until it is active again or the app is moved back."}).errors({...MEETINGS,...APP_NOT_FOUND,...CONNECT_DOMAIN}).input(MeetingAppIdInput.extend(UpdateMeetingApp.shape)).output(MeetingApp),test:base.route({method:"POST",path:"/v1/meeting-apps/{appId}/test",tags:APP_TAGS,summary:"Test a meeting server app",description:"Requests a token the way meetings will and, with `user`, looks that address up as a host; the app becomes active or failing."}).errors({...MEETINGS,...APP_NOT_FOUND,...errors({not_testable:"The app is a sign-in app, which only account connects use."})}).input(MeetingAppIdInput.extend(TestMeetingAppRequest.shape)).output(MeetingAppTestResult),delete:base.route({method:"DELETE",path:"/v1/meeting-apps/{appId}",tags:APP_TAGS,summary:"Delete a meeting app",description:"Meetings already created stay at the service. Allowed without the meetings module, so a tenant can remove an app it no longer uses.",successStatus:204}).errors({...APP_NOT_FOUND,...errors({meeting_app_in_use:"Account connections were issued through the sign-in app."})}).input(MeetingAppIdInput).output(z34.void())},meetingConnections={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/meeting-connections",tags:CONNECTION_TAGS,summary:"List an account's meeting connections"}).errors({...MEETINGS,...ACCOUNT_NOT_FOUND}).input(AccountIdInput).output(MeetingConnectionList),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/meeting-connections/{service}",tags:CONNECTION_TAGS,summary:"Remove a meeting connection",description:"Deletes the grant (and revokes it at Zoom); meetings created with it stay. Later events use the tenant's server app, if any. Allowed without the meetings module, so a tenant can remove a grant it no longer uses.",successStatus:204}).errors({...ACCOUNT_NOT_FOUND,...errors({meeting_connection_not_found:"The account has no connection to the service."})}).input(MeetingConnectionInput).output(z34.void())},meetingConnectSessions={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/meeting-connect-sessions",tags:CONNECTION_TAGS,summary:"Create a hosted meeting connect session",description:"Returns a link to the hosted page where the account's user signs in to the service; the browser then returns to redirectUri with status=success|error, service, error and state, or without redirectUri to Workwae's own confirmation page, which needs no allowlist entry. The sign-in passes a Continue page on the API origin naming the tenant, the service and the account, and must finish in the browser that pressed Continue (an HttpOnly cookie on the API origin); a callback in any other browser renders an error page and connects nothing. A session yields at most one grant: once one sign-in completed it, the others end with error=session_used, and one completing after expiresAt ends with error=session_expired. Connecting again replaces the account's grant for the service."}).errors({...MEETINGS,...ACCOUNT_NOT_FOUND,...errors({redirect_uri_not_allowed:"redirectUri is not on the tenant's allowlist.",unknown_meeting_app:"meetingAppId names no meeting app of the tenant.",meeting_app_mismatch:"meetingAppId is not a sign-in app of the service.",meeting_app_secret_unreadable:"The meetingAppId app's secret cannot be decrypted; register the app again.",provider_not_configured:"No meetingAppId is given and the platform has no app for the service."})}).input(AccountIdInput.extend(CreateMeetingConnectSession.shape)).output(SessionLink)},hostedMeetingConnectSessions={get:base.route({method:"GET",path:"/v1/hosted/meeting-connect-sessions/{token}",tags:["Hosted pages"],summary:"Read a hosted meeting connect session",spec:publicOperation}).errors(errors({meeting_connect_session_not_found:"No session has this token, or its account no longer exists."})).input(SessionTokenInput).output(MeetingConnectSessionPublic)};import*as z35 from"zod";var NOT_FOUND2=errors({oauth_app_not_found:"The OAuth app does not exist."}),CONNECT_DOMAIN2=errors({connect_domain_inactive:"useConnectDomain is true and the workspace has no active connect domain (settings.connectDomain)."}),oauthApps={list:base.route({method:"GET",path:"/v1/oauth-apps",tags:["OAuth apps"],summary:"List tenant OAuth apps"}).output(OAuthAppList),create:base.route({method:"POST",path:"/v1/oauth-apps",tags:["OAuth apps"],summary:"Register a tenant OAuth app",description:"redirectUri is the callback to register at the provider as an authorized redirect URI. It is on the API host, or on the workspace's connect domain when useConnectDomain is set: Google only verifies an app whose redirect URIs are on domains its owner controls, so an app that is to be verified signs in through the workspace's own connect domain.",successStatus:201}).errors({...CONNECT_DOMAIN2,...errors({cloud_required:"provider is microsoft and no cloud is given.",not_microsoft:"cloud or authority is given for a Google app."})}).input(CreateOAuthApp).output(OAuthApp),update:base.route({method:"PATCH",path:"/v1/oauth-apps/{appId}",tags:["OAuth apps"],summary:"Change a tenant OAuth app: name, client secret, authority, connect domain or Gmail push",description:"Accounts on the app use the new secret from their next token refresh. useConnectDomain moves the app's sign-ins to the workspace's connect domain, or back to the API host; register the new redirectUri at the provider first. A sign-in already started finishes on the host it began on. If the connect domain stops being active while an app uses it, the app's sign-ins fail with connect_domain_inactive (headless: 409) until it is active again or the app is moved back. gmailPush (Google apps) makes the app's accounts watch the topic with users.watch instead of being polled; null stops their watches and returns them to polling. Changing it wakes the app's accounts so it applies at once, and saving it again retries a watch Gmail refused."}).errors({...NOT_FOUND2,...CONNECT_DOMAIN2,...errors({not_microsoft:"authority is given for a Google app.",gmail_push_unsupported:"gmailPush is given for a Microsoft app.",push_unavailable:"gmailPush is set and Gmail push is unavailable."})}).input(OAuthAppIdInput.extend(UpdateOAuthApp.shape)).output(OAuthApp),delete:base.route({method:"DELETE",path:"/v1/oauth-apps/{appId}",tags:["OAuth apps"],summary:"Delete a tenant OAuth app",successStatus:204}).errors({...NOT_FOUND2,...errors({oauth_app_in_use:"Live accounts still use the app."})}).input(OAuthAppIdInput).output(z35.void())};import*as z36 from"zod";var NOT_FOUND3=errors({org_connection_not_found:"The organization connection does not exist."}),CONSENT_REQUIRED=errors({consent_required:"No Microsoft directory has consented to the app yet."}),CREDENTIALS=errors({invalid_service_account_key:"serviceAccountKey is not a usable Google service account key.",invalid_certificate:"The certificate or private key is not a usable PEM certificate and unencrypted PKCS#8 RSA key.",directory_id_not_allowed:"A directoryId is given for a platform connection."}),CONNECT_DOMAIN3=errors({connect_domain_inactive:"useConnectDomain is true and the workspace has no active connect domain (settings.connectDomain).",connect_domain_unsupported:"useConnectDomain is true for a Google connection or a platform Microsoft connection, whose sign-in does not run on a tenant's own app."}),TAGS2=["Organization connections"],orgConnections={list:base.route({method:"GET",path:"/v1/org-connections",tags:TAGS2,summary:"List organization connections"}).output(OrgConnectionList),create:base.route({method:"POST",path:"/v1/org-connections",tags:TAGS2,summary:"Register an organization connection",description:"Google: the service account key is checked and sealed; authorize the returned clientId for the returned scopes in the Admin console, then test. Microsoft: a platform connection starts in pending_consent (POST .../consent); a tenant app with a directoryId starts unverified.",successStatus:201}).errors({...CREDENTIALS,...CONNECT_DOMAIN3,...errors({provider_not_configured:"A platform connection names a cloud the platform has no app for."})}).input(CreateOrgConnection).output(OrgConnection),get:base.route({method:"GET",path:"/v1/org-connections/{connectionId}",tags:TAGS2,summary:"Get an organization connection"}).errors(NOT_FOUND3).input(OrgConnectionIdInput).output(OrgConnection),update:base.route({method:"PATCH",path:"/v1/org-connections/{connectionId}",tags:TAGS2,summary:"Update an organization connection",description:"Replacing a credential or the authorized scopes makes the connection unverified until the next test."}).errors({...NOT_FOUND3,...CREDENTIALS,...CONNECT_DOMAIN3,...errors({credential_mismatch:"A field does not apply to this connection's kind or credential type, or a certificate is replaced without its private key."})}).input(OrgConnectionIdInput.extend(UpdateOrgConnection.shape)).output(OrgConnection),delete:base.route({method:"DELETE",path:"/v1/org-connections/{connectionId}",tags:TAGS2,summary:"Delete an organization connection",successStatus:204}).errors({...NOT_FOUND3,...errors({connection_in_use:"Live accounts still authenticate through the connection."})}).input(OrgConnectionIdInput).output(z36.void()),consent:base.route({method:"POST",path:"/v1/org-connections/{connectionId}/consent",tags:TAGS2,summary:"Start Microsoft admin consent",description:"Returns the URL an administrator of the directory grants consent at. Afterwards the browser returns to redirectUri with status=success|error, connection_id, error and state; the connection records the consenting directory and is tested. A tenant app uses /adminconsent, with the redirect URI the connection reports (microsoft.redirectUri): on the workspace's connect domain when microsoft.useConnectDomain is set, in which case a connect domain that is no longer active fails the consent with connect_domain_inactive (409) before it starts. The platform app is consented through a sign-in (authorize with prompt=admin_consent) and records the directory named by the sign-in's ID token; it can only be started from the console, and the console user who started it has to be signed in, in the browser that returns, for the directory to be recorded. Errors on return include access_denied, consent_failed, consent_unverified, directory_mismatch and directory_already_connected (another platform connection holds the directory)."}).errors({...NOT_FOUND3,...errors({connect_domain_inactive:"The connection uses the workspace's connect domain (microsoft.useConnectDomain) and it is not active.",not_microsoft:"The connection is a Google connection.",redirect_uri_not_allowed:"redirectUri is neither on the tenant allowlist (settings.redirectUris) nor the console.",session_required:"A platform connection's consent is started by a caller that is not a console session.",provider_not_configured:"A platform connection's cloud has no platform app (the platform's configuration changed)."})}).input(OrgConnectionIdInput.extend(StartConsentRequest.shape)).output(ConsentLink),test:base.route({method:"POST",path:"/v1/org-connections/{connectionId}/test",tags:TAGS2,summary:"Test an organization connection",description:"Requests a token the way accounts will and reports each check; the connection becomes active or failing. A passing test returns accounts that were moved to reauth_required to active. Google impersonates `user` (default: the directory admin)."}).errors({...NOT_FOUND3,...CONSENT_REQUIRED,...errors({user_required:"A Google connection has neither a user nor a directory admin to test as."})}).input(OrgConnectionIdInput.extend(TestOrgConnectionRequest.shape)).output(OrgConnectionTestResult),directoryEntries:base.route({method:"GET",path:"/v1/org-connections/{connectionId}/directory-entries",tags:TAGS2,summary:"List the directory's mailboxes",description:"Microsoft: Graph /users (needs User.Read.All). Google: the Admin SDK users.list as the directory admin (needs admin.directory.user.readonly). `query` matches the start of names and addresses. A nextCursor is good for an hour, on this connection only."}).errors({...NOT_FOUND3,...CONSENT_REQUIRED,...INVALID_CURSOR,...PROVIDER_FAILURES,...errors({directory_unavailable:"The connection may not list the directory: there is no directory admin, the permission or scope is not granted, or the connection cannot get a token (test it).",provider_rejected:"The provider rejected the listing (e.g. the query).",provider_not_found:"The provider no longer has the directory."})}).input(OrgConnectionIdInput.extend(ListDirectoryEntriesQuery.shape)).output(DirectoryEntryPage),addAccounts:base.route({method:"POST",path:"/v1/org-connections/{connectionId}/accounts",tags:TAGS2,summary:"Add mailboxes as accounts",description:"Each mailbox is checked through the connection before its account is created (Google: a delegated token for it; Microsoft: its inbox). items holds one result per mailbox, in request order. Per-mailbox failures are reported there with the reason the API would answer with, e.g. invalid_grant (no such user), consent_revoked (the scopes or permissions are not granted), provider_forbidden or provider_not_found (no mailbox access), plan_limit_accounts (the plan's account limit was reached part way through; the mailboxes before it are created)."}).errors({...NOT_FOUND3,...CONSENT_REQUIRED,...bodyStorage("settings.bodyStorage"),...BILLING_PAST_DUE}).input(OrgConnectionIdInput.extend(AddMailboxesRequest.shape)).output(AddedMailboxList),addGroup:base.route({method:"POST",path:"/v1/org-connections/{connectionId}/groups",tags:TAGS2,summary:"Add a Google Group as an account",description:"Google has no API to read a group, so the group becomes an account of its own (auth method google_group) that is read from and sent through a member user's mailbox, `relayEmail`, with the connection's delegated token. Only the group's mail in that mailbox is synced (Google Groups list mail and mail sent as the group); the rest of the user's mail is never indexed. The group is added to the relay's send-as addresses so replies go out From the group (same-domain groups are accepted at once). Needs the connection's `groupsEnabled` scope authorized in the Admin console. A group has no calendar or contacts. Re-adding an existing group account re-points it at the given relay. Deleting a group account leaves its send-as alias on the relay's mailbox.",successStatus:201}).errors({...NOT_FOUND3,...planModule("Google Groups"),...bodyStorage("settings.bodyStorage"),...PLAN_LIMIT_ACCOUNTS,...BILLING_PAST_DUE,...PROVIDER_FAILURES,...errors({group_unsupported:"The connection is not a Google domain-wide delegation connection, or the group is the relay's own address.",scope_missing:"The connection lacks the gmail.settings.sharing scope (set groupsEnabled and authorize the scope in the Admin console).",group_relay_inaccessible:"The connection cannot act as the relay: it is not a user of the Workspace, or the scopes are not authorized.",group_alias_unverified:"Gmail did not accept the group as a send-as address of the relay (e.g. a group outside the Workspace domain awaiting verification).",account_exists:"The group address is already an account authenticated another way."})}).input(OrgConnectionIdInput.extend(AddGroupRequest.shape)).output(Account)},sharedMailboxes={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/shared-mailboxes",tags:["Shared mailboxes"],summary:"Open a shared mailbox with an account's grant",description:"Creates (or re-points) an account for a shared mailbox the delegating account's user can access, authenticated with that account's grant: Graph addresses it as /users/{email}; an Exchange IMAP account logs in as user\\shared with XOAUTH2. Gmail has no delegated shared mailboxes: add them through a Google domain-wide delegation connection.",successStatus:201}).errors({...planModule("shared mailboxes"),...bodyStorage("settings.bodyStorage"),...PLAN_LIMIT_ACCOUNTS,...BILLING_PAST_DUE,...accountState("The delegating account"),...PROVIDER_FAILURES,...errors({account_not_found:"The delegating account does not exist.",shared_mailbox_unsupported:"The delegating account is Gmail, password IMAP, or itself shared or organization-authenticated, or the mailbox is its own.",scope_missing:"The delegating account's grant lacks the shared mailbox scope (reconnect it with options.sharedMailboxesEnabled).",shared_mailbox_inaccessible:"The delegating account's user cannot open the mailbox (no access, no such mailbox, or the login is refused).",account_exists:"The mailbox is already an account authenticated another way."})}).input(AccountIdInput.extend(OpenSharedMailboxRequest.shape)).output(Account)};import*as z38 from"zod";import*as z37 from"zod";var SchedulingWindow=named("SchedulingWindow",z37.object({start:TimeOfDay,end:TimeOfDay}).refine((window)=>window.end>window.start,{message:"end must be after start",path:["end"],params:{reason:"invalid_working_hours"}})),DayWindows=z37.array(SchedulingWindow).max(MAX_SCHEDULING_DAY_WINDOWS),SchedulingHours=named("SchedulingHours",z37.object({mon:DayWindows.optional(),tue:DayWindows.optional(),wed:DayWindows.optional(),thu:DayWindows.optional(),fri:DayWindows.optional(),sat:DayWindows.optional(),sun:DayWindows.optional()})),DEFAULT_SCHEDULING_HOURS={mon:[{start:"09:00",end:"17:00"}],tue:[{start:"09:00",end:"17:00"}],wed:[{start:"09:00",end:"17:00"}],thu:[{start:"09:00",end:"17:00"}],fri:[{start:"09:00",end:"17:00"}]},SlotMinutes2=z37.number().int().min(MIN_SLOT_MINUTES).max(MAX_SLOT_MINUTES),BufferMinutes=z37.number().int().min(0).max(MAX_SCHEDULING_BUFFER_MINUTES),EventTemplate={title:z37.string().min(1).max(MAX_SCHEDULING_TITLE_LENGTH).default("{{invitee}}"),description:z37.string().max(MAX_SCHEDULING_DESCRIPTION_LENGTH).optional(),location:z37.string().max(MAX_EVENT_LOCATION_LENGTH).optional(),onlineMeeting:z37.boolean().default(!1),onlineMeetingService:MeetingService.optional()},SchedulingLinkFields=named("SchedulingLinkFields",z37.object({name:z37.string().min(1).max(MAX_NAME_LENGTH),accountIds:z37.array(Id).min(1).max(MAX_AVAILABILITY_ACCOUNTS).refine((ids)=>new Set(ids).size===ids.length,"accountIds lists an account twice"),mode:AvailabilityMode.default("all"),durationMinutes:SlotMinutes2.default(30),stepMinutes:SlotMinutes2.default(30),windowDays:z37.number().int().min(1).max(MAX_SCHEDULING_WINDOW_DAYS).default(14),timeZone:z37.string().min(1).max(MAX_TIME_ZONE_LENGTH),hours:SchedulingHours.default(DEFAULT_SCHEDULING_HOURS),bufferBeforeMinutes:BufferMinutes.default(0),bufferAfterMinutes:BufferMinutes.default(0),minimumNoticeMinutes:z37.number().int().min(0).max(MAX_SCHEDULING_NOTICE_MINUTES).default(0),maxPerDay:z37.number().int().min(1).max(MAX_SCHEDULING_PER_DAY).nullable().default(null),...EventTemplate})),SchedulingLink=named("SchedulingLink",SchedulingLinkFields.extend({id:Id,createdAt:Timestamp,updatedAt:Timestamp})),SchedulingLinkPage=Page(SchedulingLink),SchedulingLinkIdInput=named("SchedulingLinkIdInput",z37.object({linkId:Id})),CreateSchedulingLink=named("CreateSchedulingLink",SchedulingLinkFields),UpdateSchedulingLink=named("UpdateSchedulingLink",SchedulingLinkFields.partial().extend({description:z37.string().max(MAX_SCHEDULING_DESCRIPTION_LENGTH).nullable().optional(),location:z37.string().max(MAX_EVENT_LOCATION_LENGTH).nullable().optional(),onlineMeetingService:MeetingService.nullable().optional(),maxPerDay:z37.number().int().min(1).max(MAX_SCHEDULING_PER_DAY).nullable().optional()})),ListSchedulingLinksQuery=named("ListSchedulingLinksQuery",z37.object({limit:pageLimit(MAX_SCHEDULING_SLOTS,100),cursor:Cursor.optional()})),SchedulingSlotsQuery=named("SchedulingSlotsQuery",z37.object({from:QueryTimestamp.optional(),to:QueryTimestamp.optional(),limit:pageLimit(MAX_SCHEDULING_SLOTS,DEFAULT_SCHEDULING_SLOTS)})),SchedulingSlotsInput=withDateRange(SchedulingLinkIdInput.extend(SchedulingSlotsQuery.shape),{from:"from",to:"to",maxSpanMs:MAX_SCHEDULING_WINDOW_DAYS*DAY_MS}),SchedulingSlot=named("SchedulingSlot",z37.object({start:Timestamp,end:Timestamp,accountIds:z37.array(Id)})),SchedulingSlots=named("SchedulingSlots",z37.object({items:z37.array(SchedulingSlot)})),CreateBooking=named("CreateBooking",z37.object({start:QueryTimestamp,invitee:AddressInput,notes:z37.string().max(MAX_BOOKING_NOTES_LENGTH).optional()})),CreateBookingInput=SchedulingLinkIdInput.extend(CreateBooking.shape),BookingStatus=named("BookingStatus",z37.enum(["booked","cancelled"])),Booking=named("Booking",z37.object({id:Id,linkId:Id,accountId:Id,eventId:Id.nullable(),start:Timestamp,end:Timestamp,invitee:AddressInput,notes:z37.string().nullable(),status:BookingStatus,createdAt:Timestamp,cancelledAt:Timestamp.nullable()})),BookingIdInput=named("BookingIdInput",z37.object({bookingId:Id})),SCHEDULING_WEEKDAYS=Weekday.options;var LINK_NOT_FOUND=errors({scheduling_link_not_found:"The scheduling link does not exist."}),UNKNOWN_ACCOUNT=errors({unknown_account:"accountIds names an account the tenant does not have (data lists it)."}),LINK_INPUT=errors({invalid_time_zone:"timeZone is unknown.",invalid_working_hours:"A day's hours do not end after they start.",multiple_meeting_options:"Both onlineMeeting and onlineMeetingService are set."}),schedulingLinks={list:base.route({method:"GET",path:"/v1/scheduling-links",tags:["Scheduling"],summary:"List scheduling links",description:"Newest first."}).errors(INVALID_CURSOR).input(ListSchedulingLinksQuery).output(SchedulingLinkPage),get:base.route({method:"GET",path:"/v1/scheduling-links/{linkId}",tags:["Scheduling"],summary:"Get a scheduling link"}).errors(LINK_NOT_FOUND).input(SchedulingLinkIdInput).output(SchedulingLink),create:base.route({method:"POST",path:"/v1/scheduling-links",tags:["Scheduling"],summary:"Create a scheduling link",description:"accountIds are accounts of the tenant whose calendars the link offers. mode all books every account into one event; mode any books one of them, the one with the fewest bookings of this link in the last 30 days (any status), the earliest in accountIds when tied.",successStatus:201}).errors({...planModule("calendar"),...UNKNOWN_ACCOUNT,...LINK_INPUT}).input(CreateSchedulingLink).output(SchedulingLink),update:base.route({method:"PATCH",path:"/v1/scheduling-links/{linkId}",tags:["Scheduling"],summary:"Update a scheduling link",description:"Only the fields given change. Existing bookings keep their events."}).errors({...LINK_NOT_FOUND,...UNKNOWN_ACCOUNT,...LINK_INPUT}).input(SchedulingLinkIdInput.extend(UpdateSchedulingLink.shape)).output(SchedulingLink),delete:base.route({method:"DELETE",path:"/v1/scheduling-links/{linkId}",tags:["Scheduling"],summary:"Delete a scheduling link",description:"Bookings of the link are deleted with it. Their events are left as they are.",successStatus:204}).errors(LINK_NOT_FOUND).input(SchedulingLinkIdInput).output(z38.void()),slots:base.route({method:"GET",path:"/v1/scheduling-links/{linkId}/slots",tags:["Scheduling"],summary:"List bookable slots",description:"Slots inside the link's window (windowDays ahead of now, in its time zone), not before its minimum notice, inside its hours, clear of each account's own calendar once widened by the buffers, and under maxPerDay. from and to narrow that range. accountIds on a slot are the accounts free for it."}).errors({...LINK_NOT_FOUND,...planModule("calendar"),...errors({invalid_range:"to is not after from, or the range is longer than the link may look ahead."})}).input(SchedulingSlotsInput).output(SchedulingSlots)},bookings={create:base.route({method:"POST",path:"/v1/scheduling-links/{linkId}/bookings",tags:["Scheduling"],summary:"Book a slot",description:"Books start only when it is one of the link's slots at that moment. mode any hosts it on the account with the fewest bookings of this link in the last 30 days among those free then (the earliest in accountIds when tied); mode all creates the event on the first account and invites the others. The invitee is an attendee. The slot is claimed in the database before the event is created, so two bookings of the same account and start cannot both succeed (slot_taken). Sends booking.created.",successStatus:201}).errors({...LINK_NOT_FOUND,...planModule("calendar"),...CALENDAR_WRITE,...MEETING_SERVICE,...errors({slot_unavailable:"start is not a slot the link offers, or no account is free for it.",slot_taken:"The slot was booked meanwhile.",calendar_disabled:"The account the booking would land on has calendar sync off.",calendar_not_found:"That account has no calendar an event can be created in.",plan_module_unavailable:"The tenant's plan does not include calendar, or the link creates a Zoom or Webex meeting and the plan does not include meetings.",multiple_meeting_options:"The link sets both onlineMeeting and onlineMeetingService."})}).input(CreateBookingInput).output(Booking),cancel:base.route({method:"DELETE",path:"/v1/bookings/{bookingId}",tags:["Scheduling"],summary:"Cancel a booking",description:"Deletes the event (telling its attendees) and marks the booking cancelled. Sends booking.cancelled. An event already gone is still a successful cancel."}).errors({...planModule("calendar"),...CALENDAR_PROVIDER,...errors({booking_not_found:"The booking does not exist.",booking_cancelled:"The booking is already cancelled.",calendar_disabled:"The account's calendar sync is off, so the event cannot be cancelled."})}).input(BookingIdInput).output(Booking)};import*as z39 from"zod";var LIST="With listId, content is always rendered, `{{rcpt.unsubscribeUrl}}`, `{{rcpt.address}}` and `{{rcpt.name}}` are the recipient's, and the message carries List-Unsubscribe with that same URL and List-Unsubscribe-Post: List-Unsubscribe=One-Click (RFC 8058). Click tracking never rewrites a List-Unsubscribe URL, so the body's link stays as written. A recipient on that list or on the global list is not sent to, at queue time and again at delivery (send.failed with code suppressed). Without listId the global list is consulted only when the suppressionCheck setting (the account's, else the workspace's) is always: then a send with any to, cc or bcc address on it is refused with suppressed (data.addresses names them) and fails at delivery likewise. Whether the provider's DKIM signature covers the List-Unsubscribe headers is up to its signing.",UNDO="A message without a sendAt later than the account's undo window (undoSendSeconds) from now waits out the window; undoUntil says until when cancelling the entry (DELETE .../outbox/{outboxId}) undoes the send.",DELIVERY="Delivery is at-least-once, except outcome_unknown (below), and without order across retries: an account's entries are first tried in queue order, but one backing off after a failed attempt does not hold up the later ones. A retry first looks for a copy that already went out. When no SMTP reply comes after the final dot was handed to the operating system (the connection dropped or timed out, or the attempt ran out of time), the server may have accepted the message, so it is not sent again: send.failed carries code outcome_unknown and the client app decides whether to resend.",TRACKING="With tracking, the HTML part gets a 1x1 pixel (opens) and its http(s) links point at Workwae's redirect (clicks), reported as message.opened and message.clicked and counted on the outbox entry; text parts are left as they are. With clicks, tracking.honeypot (else the account's trackingHoneypot) also adds one hidden link only machines follow; it does nothing without clicks, so asking for it with clicks off is refused rather than ignored. Tracking is per message, not per recipient: every recipient gets the same MIME.",MAIL_MERGE=`Each of at most ${MAX_MAIL_MERGE_ENTRIES} entries becomes its own outbox entry and message, addressed to its to alone, with its own Message-ID, events, bounce matching and tracking; the answer has one result per entry, in order. Content is rendered per entry with its params (a template always; inline content only for entries that carry params, or with listId). An entry that cannot be queued fails alone, its result naming the reason (invalid_recipient, invalid_message_id, duplicate_message_id, template_render_failed, plan_limit_sends once the day's sends run out, suppressed when listId is given and the address is on that list or the global list, or the suppressionCheck setting is always and it is on the global list, internal_error), while the others go ahead; the call itself succeeds. The body is the send body without to, cc, bcc and render: an unknown field is refused.`,COMPOSE_ERRORS={...MAIL_ACCOUNT,...errors({from_not_allowed:"from is not one of the account's identities.",attachments_too_large:"The attachments exceed the provider's limit.",unknown_reply_target:"inReplyToMessageId names no message of the account."}),...accountState(),...PROVIDER_FAILURES},TEMPLATE_ERRORS=errors({unknown_template:"template names no template of the tenant.",template_account_mismatch:"The template is bound to another account.",template_and_inline_fields:"template comes with an inline subject, html, text, previewText or format.",invalid_template:"Inline content to render is not valid Handlebars or uses partials or decorators; data.field names the field.",params_too_large:`params exceed ${MAX_RENDER_PARAMS_BYTES} bytes as JSON.`,template_render_failed:`Rendering fails (a missing helper, more than ${MAX_RENDER_ITERATIONS} block iterations, a field rendered past its limit, Markdown longer than ${MAX_MARKDOWN_LENGTH} characters, rendered or not); data.field names the field.`}),RENDERED_ONCE="Content is rendered when the request is taken, so later template edits never change it.",TRACKING_ERRORS=errors({honeypot_needs_click_tracking:"tracking.honeypot is true and click tracking is off.",plan_module_unavailable:"Tracking is asked for and the tenant's plan does not include it (nor do the account's tracking defaults apply then)."}),LIST_UNSUBSCRIBE_UNAVAILABLE=errors({list_unsubscribe_unavailable:"listId is given and unsubscribe links are unavailable."}),PLAN_LIMIT_SENDS=errors({plan_limit_sends:"The tenant already queued as many sends today (UTC) as its plan allows.",plan_required:PLAN_REQUIRED_CONDITION,spend_cap_reached:spendCapCondition("a send goes beyond the sends the plan includes")}),DRAFT_NOT_FOUND=errors({draft_not_found:"The message does not exist or is no draft."}),drafts={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/drafts",tags:["Mail"],summary:"Create a draft",description:RENDERED_ONCE,successStatus:201}).errors({...COMPOSE_ERRORS,...TEMPLATE_ERRORS,...PROVIDER_CHANGE,...errors({draft_lost:"The provider took the draft but did not answer it back."})}).input(AccountIdInput.extend(ComposeWithTemplate.shape)).output(MessageSummary),update:base.route({method:"PUT",path:"/v1/accounts/{accountId}/drafts/{messageId}",tags:["Mail"],summary:"Replace a draft's content",description:RENDERED_ONCE}).errors({...COMPOSE_ERRORS,...TEMPLATE_ERRORS,...PROVIDER_CHANGE,...DRAFT_NOT_FOUND}).input(MessageIdInput.extend(ComposeWithTemplate.shape)).output(MessageSummary),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/drafts/{messageId}",tags:["Mail"],summary:"Delete a draft",successStatus:204}).errors({...MAIL_ACCOUNT,...DRAFT_NOT_FOUND,...accountState(),...PROVIDER_CHANGE}).input(MessageIdInput).output(z39.void()),send:base.route({method:"POST",path:"/v1/accounts/{accountId}/drafts/{messageId}/send",tags:["Mail"],summary:"Send a draft",description:`${UNDO} ${TRACKING} A Gmail or Microsoft 365 draft is sent by the provider as it is stored, so it is never tracked (the account's defaults do not apply); an IMAP draft is. A cancelled draft-send leaves the draft in place. With the suppressionCheck setting always, a draft with a to, cc or bcc address on the global list is refused with suppressed, and fails likewise at delivery.`,successStatus:201}).errors({...MAIL_ACCOUNT,...DRAFT_NOT_FOUND,...accountState(),...errors({not_a_draft:"The message stopped being a draft while the send was queued.",no_recipients:"The draft has no to, cc or bcc recipients (as last indexed).",suppressed:"The suppressionCheck setting is always and a to, cc or bcc address of the draft is on the global list (as last indexed). data.addresses names them.",tracking_unsupported:"Tracking (opens, clicks or honeypot) is asked for a Gmail or Microsoft 365 draft."}),...TRACKING_ERRORS,...PLAN_LIMIT_SENDS,...BILLING_PAST_DUE}).input(MessageIdInput.extend(SendOptions.shape)).output(QueuedSend)},SEND_INPUT=AccountIdInput.extend(SendMessageRequest.shape).strict().superRefine((send,ctx)=>{if(send.to.length+send.cc.length+send.bcc.length===0)ctx.addIssue({code:"custom",path:["to"],message:"The message has no recipients (to, cc or bcc)",params:{reason:"no_recipients"}});for(let name of Object.keys(send.headers??{})){let lower=name.toLowerCase();if(lower==="in-reply-to"&&send.inReplyToMessageId!==void 0)ctx.addIssue({code:"custom",path:["headers",name],message:"In-Reply-To cannot come with inReplyToMessageId, which writes it"});if(lower.startsWith("list-")&&send.listId!==void 0)ctx.addIssue({code:"custom",path:["headers",name],message:`${name} cannot come with listId, which writes the list headers`})}}),SEND_NOT_FOUND=errors({send_not_found:"The account has no such outbox entry."}),MAIL_MERGE_NOT_FOUND=errors({mail_merge_not_found:"The account has no such mail merge."}),MAIL_MERGE_KEPT="A merge is kept as long as any of its entries is in the outbox, so it goes with the last of them.",outbox={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/outbox",tags:["Mail"],summary:"Send a message",description:`Queues the message as an outbox entry; delivery is reported with send.sent, send.retrying and send.failed events carrying its id as outboxId, and a later bounce with message.bounced. ${DELIVERY} ${UNDO} ${TRACKING} ${LIST} For one message per recipient, see POST .../mail-merges. ${RENDERED_ONCE} An unknown field is refused.`,successStatus:201}).errors({...COMPOSE_ERRORS,...TEMPLATE_ERRORS,...errors({no_recipients:"to, cc and bcc are all empty.",list_send_recipients:"listId is given and the message has not exactly one to and no cc or bcc.",suppressed:"listId is given and the recipient is on that list or the global list, or the suppressionCheck setting is always and a to, cc or bcc address is on the global list. data.addresses names them.",duplicate_message_id:"messageId is already the Message-ID of another send of the account."}),...TRACKING_ERRORS,...LIST_UNSUBSCRIBE_UNAVAILABLE,...PLAN_LIMIT_SENDS,...BILLING_PAST_DUE}).input(SEND_INPUT).output(QueuedSend),list:base.route({method:"GET",path:"/v1/accounts/{accountId}/outbox",tags:["Mail"],summary:"List queued and recent sends, newest first",description:"status keeps the entries in that status."}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR}).input(AccountIdInput.extend(ListOutboxQuery.shape)).output(OutboxEntryPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/outbox/{outboxId}",tags:["Mail"],summary:"Get a send",description:"One entry with its tracking counts, for as long as the outbox keeps it: a sent message's outboxId names it."}).errors({...MAIL_ACCOUNT,...SEND_NOT_FOUND}).input(OutboxIdInput).output(OutboxEntry),cancel:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/outbox/{outboxId}",tags:["Mail"],summary:"Cancel a queued send",description:"Undoes a send while it waits for its sendAt.",successStatus:204}).errors({...MAIL_ACCOUNT,...SEND_NOT_FOUND,...errors({send_not_cancellable:"The entry is no longer queued: it is being sent, was sent, failed for good or was cancelled (the message names which)."})}).input(OutboxIdInput).output(z39.void()),reschedule:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/outbox/{outboxId}",tags:["Mail"],summary:"Reschedule a queued send",description:"Moves sendAt, earlier or later; a past sendAt sends as soon as possible. Announced as send.rescheduled."}).errors({...MAIL_ACCOUNT,...SEND_NOT_FOUND,...errors({send_not_reschedulable:"The entry is no longer queued (it is being sent, was sent, failed for good or was cancelled), or it is queued again after a failed attempt; the message names which."})}).input(OutboxIdInput.extend(RescheduleSendRequest.shape)).output(OutboxEntry),tracking:base.route({method:"GET",path:"/v1/accounts/{accountId}/outbox/{outboxId}/tracking",tags:["Mail"],summary:"List a send's opens and clicks",description:"Newest first. At most 1000 are kept per send (the counts on the entry keep going), each for the tracking retention period. Each event carries what its request showed: the method (GET or HEAD), the Referer and Origin hosts, the mail client (Gmail's image proxy, web or app; Outlook on the web, desktop or mobile; Apple Mail and its Mail Privacy Protection; Yahoo, AOL, Thunderbird, Proton, Fastmail, Zoho, iCloud, a self-hosted webmail; else unknown), the device, OS and browser families, Cloudflare's visitor location (country, region, city, timezone, latitude and longitude, behind a trusted proxy only), the seconds after the send and since the send's previous hit, its sequence number, whether it was conditional, a Range request or a prefetch, the sec-fetch tuple, and the request headers (lower-cased, values cut at 1024 characters, at most 64, never Cookie, Authorization or Proxy-Authorization, forwarding and cf-* headers only from the trusted proxy; the full Referer is only here). Full addresses and headers stay with the tenant for the retention period."}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR,...SEND_NOT_FOUND}).input(OutboxIdInput.extend(ListTrackingEventsQuery.shape)).output(TrackingEventPage)},mailMerges={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/mail-merges",tags:["Mail"],summary:"Send one message per recipient (mail merge)",description:`${MAIL_MERGE} ${DELIVERY} ${UNDO} ${TRACKING} ${LIST} What holds for the whole call is checked once and refuses it. ${RENDERED_ONCE} Answers 200 with the merge's id (GET .../mail-merges/{mailMergeId}, POST .../cancel to cancel what is still queued) and a result per entry. ${MAIL_MERGE_KEPT}`}).errors({...COMPOSE_ERRORS,...TEMPLATE_ERRORS,...errors({attachments_too_large:`The attachments exceed the provider's limit, or the attachments times the entries exceed ${MAX_MAIL_MERGE_ATTACHMENT_BYTES} bytes.`}),...TRACKING_ERRORS,...LIST_UNSUBSCRIBE_UNAVAILABLE,...BILLING_PAST_DUE}).input(AccountIdInput.extend(MailMergeRequest.shape).strict()).output(MailMergeResultList),list:base.route({method:"GET",path:"/v1/accounts/{accountId}/mail-merges",tags:["Mail"],summary:"List mail merges, newest first",description:MAIL_MERGE_KEPT}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR}).input(AccountIdInput.extend(ListMailMergesQuery.shape)).output(MailMergePage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/mail-merges/{mailMergeId}",tags:["Mail"],summary:"Get a mail merge",description:`The merge with its entries counted by outbox status. ${MAIL_MERGE_KEPT}`}).errors({...MAIL_ACCOUNT,...MAIL_MERGE_NOT_FOUND}).input(MailMergeIdInput).output(MailMerge),cancel:base.route({method:"POST",path:"/v1/accounts/{accountId}/mail-merges/{mailMergeId}/cancel",tags:["Mail"],summary:"Cancel a mail merge's queued entries",description:"Cancels every entry of the merge that is still queued, as DELETE .../outbox/{outboxId} does for one (the payload is dropped, send.cancelled is announced for each). Entries being sent, sent, failed or cancelled are left alone; cancelled is how many this call cancelled, 0 when none was left."}).errors({...MAIL_ACCOUNT,...MAIL_MERGE_NOT_FOUND}).input(MailMergeIdInput).output(MailMergeCancelled)};var settings={get:base.route({method:"GET",path:"/v1/settings",tags:["Settings"],summary:"Get tenant settings"}).output(TenantSettings),update:base.route({method:"PATCH",path:"/v1/settings",tags:["Settings"],summary:"Update tenant settings",description:"bodyStorage applies to every account without its own override; changing it restarts their syncs (full stores and backfills every indexed message, none deletes stored content). mcpSendPolicy limits what agents send through the MCP server only (/v1 sends are not held to it); its keys present change, absent ones stay. Requires the admin scope."}).errors({...PLAN_LIMIT_ACCOUNTS,...bodyStorage("bodyStorage"),...errors({redirect_uri_not_allowed:"A redirect URI is not absolute https (http only for localhost)."})}).input(UpdateTenantSettings).output(TenantSettings)};import*as z40 from"zod";var base2=base.errors(planModule("mail")),LIST_NOT_FOUND=errors({suppression_list_not_found:"The list does not exist (the global list has none to change)."}),REVIEW_NOT_FOUND=errors({suppression_review_not_found:"The review does not exist."}),suppressions={list:base2.route({method:"GET",path:"/v1/suppressions",tags:["Suppressions"],summary:"List suppression lists",description:"The global list first, then every named list by listId, with its counts by source."}).output(SuppressionListList),create:base2.route({method:"POST",path:"/v1/suppressions",tags:["Suppressions"],summary:"Create a suppression list",description:"id is what sends name as listId; it cannot change later, the name and description can. A send or add naming a list that does not exist creates it too, named after its id.",successStatus:201}).errors(errors({suppression_list_exists:"A list with that id exists."})).input(CreateSuppressionList).output(SuppressionList),update:base2.route({method:"PATCH",path:"/v1/suppressions/{listId}",tags:["Suppressions"],summary:"Update a suppression list",description:"Only the fields given change; description null clears it. The global list cannot be changed."}).errors(LIST_NOT_FOUND).input(ListIdInput.extend(UpdateSuppressionList.shape)).output(SuppressionList),delete:base2.route({method:"DELETE",path:"/v1/suppressions/{listId}",tags:["Suppressions"],summary:"Delete a suppression list",description:"Deletes the list and its pending reviews and, with ?withEntries=true, every address on it: later sends naming it reach those addresses again (unless the global list holds them), and the next one naming it creates it anew. The global list cannot be deleted.",successStatus:204,inputStructure:"detailed"}).errors({...LIST_NOT_FOUND,...errors({suppression_list_not_empty:"The list holds addresses and withEntries is not true."})}).input(DeleteSuppressionListInput).output(z40.void()),entries:{list:base2.route({method:"GET",path:"/v1/suppressions/{listId}/entries",tags:["Suppressions"],summary:"List suppressed addresses",description:"Addresses on one list, by address; a list without addresses is empty, not missing. address keeps those containing it, source those that came from it."}).errors(INVALID_CURSOR).input(SuppressionListIdInput.extend(ListSuppressionEntriesQuery.shape)).output(SuppressionEntryPage),add:base2.route({method:"POST",path:"/v1/suppressions/{listId}/entries",tags:["Suppressions"],summary:"Suppress addresses",description:`Puts up to ${MAX_SUPPRESSION_BATCH} addresses on the list in one call; a repeated address counts once. added counts the addresses put on the list, alreadyListed those that were on it already, which are left as they were. A list that does not exist is created, named after its listId. Answers 200: entries are listed, not fetched one by one. Emits no suppression.created: that event reports what recipients and bounces did.`}).input(SuppressionListIdInput.extend(AddSuppressionEntriesRequest.shape)).output(SuppressionEntriesAdded),remove:base2.route({method:"DELETE",path:"/v1/suppressions/{listId}/entries/{address}",tags:["Suppressions"],summary:"Release an address",description:"Later sends naming the list reach the address again (unless another list they consult holds it).",successStatus:204}).errors(errors({suppression_entry_not_found:"The address is not on the list."})).input(SuppressionEntryInput).output(z40.void())}},suppressionReviews={list:base2.route({method:"GET",path:"/v1/suppression-reviews",tags:["Suppressions"],summary:"List opt-outs to review",description:"Detected opt-outs waiting for a person, newest first; listId keeps one list's. Holds AI-classified opt-out replies while optOutHandling is review, and auto-replies saying the person is no longer at the address (source inactive, on the global list) while it is review or suppress. A detection of an address already on its list, or already waiting, is not queued again."}).errors(INVALID_CURSOR).input(ListSuppressionReviewsQuery).output(SuppressionReviewPage),approve:base2.route({method:"POST",path:"/v1/suppression-reviews/{reviewId}/approve",tags:["Suppressions"],summary:"Approve an opt-out",description:"Puts the address on the review's list with the review's source and removes the review. added is false when the address was on the list already. Emits suppression.created when it was not."}).errors(REVIEW_NOT_FOUND).input(ReviewIdInput).output(SuppressionAdded),dismiss:base2.route({method:"POST",path:"/v1/suppression-reviews/{reviewId}/dismiss",tags:["Suppressions"],summary:"Dismiss an opt-out",description:"Removes the review without suppressing anything; a later detection of the same address queues it again.",successStatus:204}).errors(REVIEW_NOT_FOUND).input(ReviewIdInput).output(z40.void())};import*as z41 from"zod";var base3=base.errors(planModule("mail")),NOT_FOUND4=errors({template_not_found:"The template does not exist."}),SYNTAX=errors({invalid_template:"A field is not valid Handlebars or uses partials or decorators (data names the field, the message is the parser's)."}),NAME_TAKEN=errors({template_name_taken:"The scope (the tenant, or the bound account) already has a template of that name."}),UNKNOWN_ACCOUNT2=errors({unknown_account:"The accountId names an account the tenant does not have (data lists it)."}),templates={list:base3.route({method:"GET",path:"/v1/templates",tags:["Templates"],summary:"List templates",description:"Newest first, without their content (GET /v1/templates/{templateId} has it). accountId lists those bound to that account; usableBy those the account may send with (the tenant's shared ones and its own); name the ones of that exact name, one per scope; query those whose name contains it, ignoring case."}).errors(INVALID_CURSOR).input(ListTemplatesQuery).output(TemplateSummaryPage),get:base3.route({method:"GET",path:"/v1/templates/{templateId}",tags:["Templates"],summary:"Get a template"}).errors(NOT_FOUND4).input(TemplateIdInput).output(Template),create:base3.route({method:"POST",path:"/v1/templates",tags:["Templates"],summary:"Create a template",description:"Every field is checked to compile as Handlebars.",successStatus:201}).errors({...SYNTAX,...UNKNOWN_ACCOUNT2,...NAME_TAKEN}).input(CreateTemplate).output(Template),update:base3.route({method:"PATCH",path:"/v1/templates/{templateId}",tags:["Templates"],summary:"Update a template",description:"Only the fields given change; queued sends keep what they were rendered with."}).errors({...NOT_FOUND4,...SYNTAX,...NAME_TAKEN}).input(TemplateIdInput.extend(UpdateTemplate.shape)).output(Template),delete:base3.route({method:"DELETE",path:"/v1/templates/{templateId}",tags:["Templates"],summary:"Delete a template",description:"Queued sends keep their rendered content.",successStatus:204}).errors(NOT_FOUND4).input(TemplateIdInput).output(z41.void())},templateRenders={create:base3.route({method:"POST",path:"/v1/template-renders",tags:["Templates"],summary:"Render a template without sending",description:"Renders a stored template (templateId) or inline fields with params, as a send would. account.email and account.name come from accountId, else from the template's own account, else render empty."}).errors({...SYNTAX,...UNKNOWN_ACCOUNT2,...errors({template_and_inline_fields:"The templateId comes with inline fields or format.",unknown_template:"The templateId names no template of the tenant.",template_account_mismatch:"The template is bound to another account than accountId.",params_too_large:`The params exceed ${MAX_RENDER_PARAMS_BYTES} bytes as JSON.`,template_render_failed:`Rendering fails: a missing helper, more than ${MAX_RENDER_ITERATIONS} block iterations, a field rendered past its limit, or Markdown longer than ${MAX_MARKDOWN_LENGTH} characters (data names the field).`})}).input(RenderTemplateRequest).output(RenderedTemplate)};import*as z43 from"zod";import*as z42 from"zod";var ThreadSummary=named("ThreadSummary",z42.object({id:ProviderId,subject:z42.string().nullable(),snippet:z42.string().nullable(),participants:z42.array(Address),messageCount:z42.number().int(),unreadCount:z42.number().int(),isFlagged:z42.boolean(),hasAttachments:z42.boolean(),hasDrafts:z42.boolean(),mailboxIds:z42.array(Id),latestMessageId:Id,lastMessageAt:Timestamp.nullable()})),Thread=named("Thread",ThreadSummary.extend({messages:z42.array(MessageSummary)})),ThreadSummaryPage=Page(ThreadSummary),ThreadIdInput=named("ThreadIdInput",AccountIdInput.extend({threadId:ProviderId})),ListThreadsQuery=named("ListThreadsQuery",z42.object({mailboxId:Id.optional(),limit:pageLimit(MAX_THREAD_PAGE_SIZE,DEFAULT_THREAD_PAGE_SIZE),cursor:Cursor.optional()})),UpdateThread=named("UpdateThread",z42.object({isRead:z42.boolean().optional(),mailboxId:Id.optional()}));var THREAD_NOT_FOUND=errors({thread_not_found:"The account has no message in the thread."}),ACTION_ERRORS={...MAIL_ACCOUNT,...THREAD_NOT_FOUND,...accountState(),...PROVIDER_CHANGE},PART_WAY="A failure part way leaves the messages done so far changed; repeating the call finishes the rest.",threads={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/threads",tags:["Mail"],summary:"List threads, latest activity first",description:"Covers the account's sync window. With mailboxId, lists threads with a message in that mailbox, ordered by their latest message there; each summary still describes the whole thread."}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR,...UNKNOWN_MAILBOX}).input(AccountIdInput.extend(ListThreadsQuery.shape)).output(ThreadSummaryPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/threads/{threadId}",tags:["Mail"],summary:"Get a thread with its messages, oldest first"}).errors({...MAIL_ACCOUNT,...THREAD_NOT_FOUND}).input(ThreadIdInput).output(Thread),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/threads/{threadId}",tags:["Mail"],summary:"Mark a thread read or unread, or move it",description:`Applies the change to every message of the thread through the message operations; messages already in that state are skipped. mailboxId moves each message there (Gmail: replaces its folder-like labels). ${PART_WAY}`}).errors({...ACTION_ERRORS,...UNKNOWN_MAILBOX}).input(ThreadIdInput.extend(UpdateThread.shape)).output(Thread),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/threads/{threadId}",tags:["Mail"],summary:"Move a thread to Trash",description:`Moves every message of the thread that is not already there to Trash. ${PART_WAY}`,successStatus:204}).errors(ACTION_ERRORS).input(ThreadIdInput).output(z43.void())};import*as z45 from"zod";import*as z44 from"zod";var TrackingDomainHostname=named("TrackingDomainHostname",customDomainHostname("track.example.com")),TrackingDomainStatus=named("TrackingDomainStatus",z44.enum(CUSTOM_DOMAIN_STATUSES)),TrackingDomainRecord=named("TrackingDomainRecord",customDomainRecord()),TrackingDomain=named("TrackingDomain",z44.object({hostname:TrackingDomainHostname,status:TrackingDomainStatus,records:z44.array(TrackingDomainRecord),ownership:TrackingDomainRecord,error:z44.string().nullable(),activatedAt:Timestamp.nullable(),createdAt:Timestamp})),SetTrackingDomain=named("SetTrackingDomain",z44.object({hostname:TrackingDomainHostname}));var UNAVAILABLE2=errors({tracking_domain_unavailable:"The deployment has no Cloudflare for SaaS configured for tracking domains."}),NOT_FOUND5=errors({tracking_domain_not_found:"The workspace has no tracking domain."}),EDGE2=errors({tracking_domain_edge_unavailable:"The edge provider (Cloudflare) refused or could not be reached."}),trackingDomain={get:base.route({method:"GET",path:"/v1/settings/tracking-domain",tags:["Settings"],summary:"Get the workspace's tracking domain",description:"The workspace's own host for tracking pixels, click links and unsubscribe URLs, with the DNS records to publish and where setting it up stands. Requires the admin scope."}).errors({...UNAVAILABLE2,...NOT_FOUND5}).output(TrackingDomain),set:base.route({method:"PUT",path:"/v1/settings/tracking-domain",tags:["Settings"],summary:"Set the workspace's tracking domain",description:"Registers the hostname with the edge and answers the DNS records to publish: a CNAME to the deployment's tracking host (records), any TXT records the edge asks for (records), and the ownership TXT record that is random to this claim (ownership). The domain is pending until the ownership record is published and the edge reports the hostname and its certificate active (checked every few minutes, or on POST /v1/settings/tracking-domain/refresh); only then do new tracked sends link to it. Replaces the workspace's current tracking domain once the new hostname is registered, so a refusal leaves the current one serving; the replaced one stops serving then, and mail already sent keeps the host it was sent with. Needs the plan's tracking module. Requires the admin scope."}).errors({...UNAVAILABLE2,...EDGE2,...planModule("tracking"),...errors({tracking_domain_not_allowed:"The hostname is one of the deployment's own, or a name the edge cannot take.",tracking_domain_taken:"Another workspace uses the hostname, or this workspace has it as its connect domain."})}).input(SetTrackingDomain).output(TrackingDomain),delete:base.route({method:"DELETE",path:"/v1/settings/tracking-domain",tags:["Settings"],summary:"Remove the workspace's tracking domain",description:"Removes the hostname from the edge and the workspace. New tracked sends use the deployment's tracking host again; links already in sent mail on the removed domain stop working. Requires the admin scope.",successStatus:204}).errors({...UNAVAILABLE2,...NOT_FOUND5,...EDGE2}).output(z45.void()),refresh:base.route({method:"POST",path:"/v1/settings/tracking-domain/refresh",tags:["Settings"],summary:"Check the workspace's tracking domain now",description:"Reads the hostname's state from the edge now instead of waiting for the next check, and answers the updated domain. Requires the admin scope."}).errors({...UNAVAILABLE2,...NOT_FOUND5,...EDGE2}).output(TrackingDomain)};import*as z46 from"zod";var BillingPeriod=named("BillingPeriod",z46.object({start:Timestamp,end:Timestamp,firstDay:DateOnly})),EffectivePlan=named("EffectivePlan",z46.object({id:Id.nullable(),name:z46.string(),limits:PlanLimits})),UsageDay=named("UsageDay",z46.object({day:DateOnly,liveAccounts:z46.number().int(),mailAccounts:z46.number().int(),calendarContactsAccounts:z46.number().int(),meetingAccounts:z46.number().int(),apiCalls:z46.record(z46.string(),z46.number().int()),sends:z46.number().int(),webhookDeliveries:z46.number().int(),aiClassifications:z46.number().int(),aiDrafts:z46.number().int(),emailValidations:z46.number().int(),storedBytes:z46.number().int().nullable()})),ValidationCreditBalance=named("ValidationCreditBalance",z46.object({included:z46.number().int().min(0),includedExpiresAt:Timestamp.nullable(),purchased:z46.number().int().min(0)})),CurrentUsage=named("CurrentUsage",z46.object({accounts:z46.number().int(),webhookEndpoints:z46.number().int(),sendsToday:z46.number().int(),emailValidationsToday:z46.number().int(),validationCredits:ValidationCreditBalance.nullable(),storedBytes:z46.number().int()})),Usage=named("Usage",z46.object({plan:EffectivePlan,current:CurrentUsage,days:z46.array(UsageDay),period:BillingPeriod.nullable()})),DAY_MS2=86400000,UsageQuery=named("UsageQuery",withDateRange(z46.object({from:DateOnly.optional(),to:DateOnly.optional()}),{from:"from",to:"to",inclusive:!0,maxSpanMs:(MAX_USAGE_RANGE_DAYS-1)*DAY_MS2}).superRefine((q,ctx)=>{if(q.from===void 0||q.to!==void 0)return;let span=Date.parse(new Date().toISOString().slice(0,10))-Date.parse(q.from);if(span<0||span>(MAX_USAGE_RANGE_DAYS-1)*DAY_MS2)ctx.addIssue({code:"custom",path:["from"],message:span<0?"from must be on or before today when to is left out":`The range may span at most ${MAX_USAGE_RANGE_DAYS} days`,params:{reason:"invalid_range"}})}));var usage={get:base.route({method:"GET",path:"/v1/usage",tags:["Usage"],summary:"Read usage and plan limits",description:"Per UTC day: live accounts (sampled), API calls by scope, sends, webhook deliveries, AI classifications and drafts, and stored bytes (listed once a day). from and to default to the last 30 days; today's counters are live."}).errors(errors({invalid_range:`from is after to (or after today, with to left out), or the range spans more than ${MAX_USAGE_RANGE_DAYS} days.`})).input(UsageQuery).output(Usage)};import*as z48 from"zod";import*as z47 from"zod";var ADDRESS_PATTERN=/^(?:[^\s@]+)?@[^\s@.]+(?:\.[^\s@.]+)+$/,WEBHOOK_HEADERS={id:"webhook-id",timestamp:"webhook-timestamp",signature:"webhook-signature"},WEBHOOK_SECRET_PREFIX="whsec_",AddressPattern=z47.string().trim().toLowerCase().max(MAX_EMAIL_LENGTH).regex(ADDRESS_PATTERN,"Expected an address or @domain"),WebhookMessageFilters=named("WebhookMessageFilters",z47.object({mailboxIds:z47.array(Id),mailboxRoles:z47.array(MailboxRole),from:z47.array(z47.string()),to:z47.array(z47.string()),subjectContains:z47.array(z47.string()),hasAttachments:z47.boolean().nullable(),direction:MessageDirection.nullable()})),WebhookFilters=named("WebhookFilters",z47.object({eventTypes:z47.array(EventType),accountIds:z47.array(Id),message:WebhookMessageFilters})),WebhookMessageFiltersInput=named("WebhookMessageFiltersInput",z47.object({mailboxIds:z47.array(Id).max(MAX_WEBHOOK_FILTER_VALUES).optional(),mailboxRoles:z47.array(MailboxRole).max(MailboxRole.options.length).optional(),from:z47.array(AddressPattern).max(MAX_WEBHOOK_FILTER_VALUES).optional(),to:z47.array(AddressPattern).max(MAX_WEBHOOK_FILTER_VALUES).optional(),subjectContains:z47.array(z47.string().trim().min(1).max(MAX_SUBJECT_FILTER_LENGTH)).max(MAX_WEBHOOK_FILTER_VALUES).optional(),hasAttachments:z47.boolean().nullable().optional(),direction:MessageDirection.nullable().optional()})),WebhookFiltersInput=named("WebhookFiltersInput",z47.object({eventTypes:z47.array(EventType).max(MAX_WEBHOOK_FILTER_VALUES).optional(),accountIds:z47.array(Id).max(MAX_WEBHOOK_FILTER_VALUES).optional(),message:WebhookMessageFiltersInput.optional()})),WebhookEndpoint=named("WebhookEndpoint",z47.object({id:Id,url:z47.url(),description:z47.string().nullable(),filters:WebhookFilters,isEnabled:z47.boolean(),disabledReason:z47.string().nullable(),lastSuccessAt:Timestamp.nullable(),failingSince:Timestamp.nullable(),previousSecretExpiresAt:Timestamp.nullable(),createdAt:Timestamp})),WebhookEndpointList=List(WebhookEndpoint),WebhookEndpointWithSecret=named("WebhookEndpointWithSecret",z47.object({endpoint:WebhookEndpoint,secret:z47.string()})),WebhookSecret=named("WebhookSecret",z47.object({secret:z47.string(),previousSecretExpiresAt:Timestamp.nullable()})),RotateWebhookSecretRequest=named("RotateWebhookSecretRequest",z47.object({graceSeconds:z47.number().int().min(0).max(MAX_WEBHOOK_SECRET_GRACE_SECONDS).default(DEFAULT_WEBHOOK_SECRET_GRACE_SECONDS)})),WebhookEndpointIdInput=named("WebhookEndpointIdInput",z47.object({endpointId:Id})),CreateWebhookEndpoint=named("CreateWebhookEndpoint",z47.object({url:z47.url(),description:z47.string().max(MAX_NOTE_LENGTH).optional(),filters:WebhookFiltersInput.optional()})),UpdateWebhookEndpoint=named("UpdateWebhookEndpoint",z47.object({url:z47.url().optional(),description:z47.string().max(MAX_NOTE_LENGTH).nullable().optional(),filters:WebhookFiltersInput.optional(),isEnabled:z47.boolean().optional()})),SendTestEventRequest=named("SendTestEventRequest",z47.object({eventType:EventType.default(TEST_EVENT_TYPE),accountId:Id.optional(),data:z47.looseObject({}).optional()}).superRefine((input,ctx)=>{if(input.data===void 0)return;let parsed=EVENTS[input.eventType].data.safeParse(input.data);if(parsed.success)return;for(let issue of parsed.error.issues)ctx.addIssue({code:"custom",message:issue.message,path:["data",...issue.path]})})),TestEventSent=named("TestEventSent",z47.object({eventId:EventId})),WebhookAttemptResult=named("WebhookAttemptResult",z47.enum(["succeeded","failed"])),WebhookAttempt=named("WebhookAttempt",z47.object({id:Id,eventId:EventId,eventType:EventType,accountId:Id.nullable(),attempt:z47.number().int(),statusCode:z47.number().int().nullable(),error:z47.string().nullable(),responseExcerpt:z47.string().nullable(),durationMs:z47.number().int(),isReplay:z47.boolean(),isTest:z47.boolean(),occurredAt:Timestamp})),WebhookAttemptPage=Page(WebhookAttempt),ListWebhookAttemptsQuery=named("ListWebhookAttemptsQuery",z47.object({limit:pageLimit(MAX_WEBHOOK_ATTEMPT_PAGE_SIZE,DEFAULT_WEBHOOK_ATTEMPT_PAGE_SIZE),cursor:Cursor.optional(),eventType:EventType.optional(),eventId:EventId.optional(),accountId:Id.optional(),result:WebhookAttemptResult.optional(),since:QueryTimestamp.optional(),until:QueryTimestamp.optional()})),ListWebhookAttemptsInput=withDateRange(WebhookEndpointIdInput.extend(ListWebhookAttemptsQuery.shape),{from:"since",to:"until"}),ReplayEventInput=named("ReplayEventInput",WebhookEndpointIdInput.extend({eventId:EventId})),ReplayRangeRequest=named("ReplayRangeRequest",withDateRange(z47.object({since:QueryTimestamp,until:QueryTimestamp}),{from:"since",to:"until"})),ReplayResult=named("ReplayResult",z47.object({queued:z47.number().int(),alreadyQueued:z47.number().int()})),FailedDeliveriesQuery=named("FailedDeliveriesQuery",withDateRange(z47.object({since:QueryTimestamp.optional(),until:QueryTimestamp.optional()}),{from:"since",to:"until"})),FailedDeliveryCount=named("FailedDeliveryCount",z47.object({count:z47.number().int()}));var ENDPOINT_NOT_FOUND=errors({webhook_endpoint_not_found:"The endpoint does not exist."}),ENDPOINT_DISABLED=errors({endpoint_disabled:"The endpoint is disabled."}),HTTPS_REQUIRED=errors({https_required:"url is not https."}),WEBHOOK_FILTER_ERRORS=errors({unknown_account:"filters.accountIds names an account the tenant does not have; data.ids lists them.",unknown_mailbox:"filters.message.mailboxIds names a mailbox the tenant does not have; data.ids lists them.",mailbox_outside_accounts:"filters.message.mailboxIds names a mailbox of an account that filters.accountIds leaves out."}),webhooks={list:base.route({method:"GET",path:"/v1/webhooks",tags:["Webhooks"],summary:"List webhook endpoints"}).output(WebhookEndpointList),create:base.route({method:"POST",path:"/v1/webhooks",tags:["Webhooks"],summary:"Create a webhook endpoint",description:"The signing secret is returned once.",successStatus:201}).errors({...HTTPS_REQUIRED,...WEBHOOK_FILTER_ERRORS,...errors({plan_limit_webhooks:"The tenant already has as many endpoints as its plan allows."})}).input(CreateWebhookEndpoint).output(WebhookEndpointWithSecret),update:base.route({method:"PATCH",path:"/v1/webhooks/{endpointId}",tags:["Webhooks"],summary:"Update a webhook endpoint",description:"The mailbox check runs whenever filters.message.mailboxIds or filters.accountIds changes."}).errors({...ENDPOINT_NOT_FOUND,...HTTPS_REQUIRED,...WEBHOOK_FILTER_ERRORS}).input(WebhookEndpointIdInput.extend(UpdateWebhookEndpoint.shape)).output(WebhookEndpoint),delete:base.route({method:"DELETE",path:"/v1/webhooks/{endpointId}",tags:["Webhooks"],summary:"Delete a webhook endpoint",successStatus:204}).errors(ENDPOINT_NOT_FOUND).input(WebhookEndpointIdInput).output(z48.void()),rotateSecret:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/rotate-secret",tags:["Webhooks"],summary:"Rotate the signing secret",description:`Deliveries are signed with the new secret at once. For graceSeconds (default ${DEFAULT_WEBHOOK_SECRET_GRACE_SECONDS}, at most ${MAX_WEBHOOK_SECRET_GRACE_SECONDS}) they also carry a signature by the old one, in the same webhook-signature header, so receivers can switch secrets without refusing a delivery; 0 stops the old secret at once. A rotation during another's grace ends that grace: only the secret it replaces is kept.`}).errors(ENDPOINT_NOT_FOUND).input(RotateWebhookSecretRequest.safeExtend(WebhookEndpointIdInput.shape)).output(WebhookSecret),test:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/test",tags:["Webhooks"],summary:"Send a test event",description:`Queues one event of eventType for delivery to this endpoint only, whatever its filters, signed like any other, with "isTest": true in its body; its attempts show in the delivery log. It carries the sample data for its type unless data is given, which must match the type's data. Test events are never in the change feed.`,successStatus:202}).errors({...ENDPOINT_NOT_FOUND,...ENDPOINT_DISABLED,...errors({unknown_account:"accountId names an account the tenant does not have; data.ids lists it."})}).input(SendTestEventRequest.safeExtend(WebhookEndpointIdInput.shape)).output(TestEventSent),attempts:base.route({method:"GET",path:"/v1/webhooks/{endpointId}/attempts",tags:["Webhooks"],summary:"Read the delivery log",description:"Delivery attempts to this endpoint, newest first: of the last 500 within 7 days, those matching the filters. A cursor stays valid while its attempt is in the log."}).errors({...ENDPOINT_NOT_FOUND,...INVALID_CURSOR,...errors({invalid_range:"until is not after since."})}).input(ListWebhookAttemptsInput).output(WebhookAttemptPage),replay:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/replay",tags:["Webhooks"],summary:"Replay a time range",description:"Queues every event in [since, until) that the endpoint would receive with its current filters again, with the same id and body as the first delivery and a fresh signature. Events the endpoint already has queued are not queued twice. Events past the tenant's eventRetentionDays cannot be replayed.",successStatus:202}).errors({...ENDPOINT_NOT_FOUND,...ENDPOINT_DISABLED,...errors({invalid_range:"until is not after since.",replay_range_too_large:`The range holds more than ${MAX_REPLAY_EVENTS} events.`})}).input(ReplayRangeRequest.safeExtend(WebhookEndpointIdInput.shape)).output(ReplayResult),replayEvent:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/events/{eventId}/replay",tags:["Webhooks"],summary:"Replay one event",description:"Queues the event again for this endpoint, whatever its filters, with the same id and body as the first delivery and a fresh signature; not twice if the endpoint already has it queued. Events past the tenant's eventRetentionDays cannot be replayed.",successStatus:202}).errors({...ENDPOINT_NOT_FOUND,...ENDPOINT_DISABLED,...errors({event_not_found:"The event is unknown or no longer retained."})}).input(ReplayEventInput).output(ReplayResult),countFailed:base.route({method:"GET",path:"/v1/webhooks/{endpointId}/failed-deliveries/count",tags:["Webhooks"],summary:"Count failed deliveries",description:"How many events replay-failed would queue now: those the endpoint gave up on (retries exhausted, or given up when it was disabled) that are still retained and not queued again, optionally only those created in [since, until)."}).errors({...ENDPOINT_NOT_FOUND,...errors({invalid_range:"until is not after since."})}).input(FailedDeliveriesQuery.safeExtend(WebhookEndpointIdInput.shape)).output(FailedDeliveryCount),replayFailed:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/replay-failed",tags:["Webhooks"],summary:"Replay failed deliveries",description:"Queues every event the endpoint gave up on again, once each, optionally only those created in [since, until), with the same id and body as the first delivery and a fresh signature. The failed deliveries are replaced by the replays (their attempts stay in the delivery log), so calling again does not send them twice. Events past the tenant's eventRetentionDays cannot be replayed.",successStatus:202}).errors({...ENDPOINT_NOT_FOUND,...ENDPOINT_DISABLED,...errors({invalid_range:"until is not after since.",replay_range_too_large:`More than ${MAX_REPLAY_EVENTS} events failed; replay a narrower range.`})}).input(FailedDeliveriesQuery.safeExtend(WebhookEndpointIdInput.shape)).output(ReplayResult)};var v1Contract={accounts,mcpKeys,mailboxes,messages,search,threads,drafts,outbox,mailMerges,templates,templateRenders,suppressions,suppressionReviews,identities,mailSettings,calendars,calendarEvents,availability,schedulingLinks,bookings,meetingConnections,meetingConnectSessions,meetingApps,contacts,contactFolders,webhooks,changes,settings,trackingDomain,connectDomain,oauthApps,connectSessions,oauthAuthorizations,orgConnections,sharedMailboxes,imapAccounts,imapSettings,autoconfig,hostedConnectSessions,hostedMeetingConnectSessions,exports,usage,ai,emailValidations,auditLog};var WEBHOOK_TOLERANCE_SECONDS=300,WEBHOOK_VERIFICATION_MESSAGES={missing_headers:"The webhook headers are missing",timestamp:"The webhook timestamp is outside the tolerance",signature:"No webhook signature matches the secret",payload:"The webhook body is not a Workwae event"};class WebhookVerificationError extends Error{failure;constructor(failure,message=WEBHOOK_VERIFICATION_MESSAGES[failure]){super(message);this.failure=failure;this.name="WebhookVerificationError"}}var encoder=new TextEncoder;function header(headers,name){if(headers instanceof Headers)return headers.get(name);for(let[key,value]of Object.entries(headers))if(key.toLowerCase()===name)return Array.isArray(value)?value.join(" "):value??null;return null}function fromBase64(text){try{return Uint8Array.from(atob(text),(c)=>c.charCodeAt(0))}catch{return null}}async function mac(secret,id,timestamp,body){let keyBytes=fromBase64(secret.startsWith(WEBHOOK_SECRET_PREFIX)?secret.slice(WEBHOOK_SECRET_PREFIX.length):secret);if(!keyBytes||keyBytes.length===0)throw TypeError("The webhook secret is not base64");let key=await crypto.subtle.importKey("raw",keyBytes,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),prefix=encoder.encode(`${id}.${timestamp}.`),payload=typeof body==="string"?encoder.encode(body):new Uint8Array(body),signed=new Uint8Array(prefix.length+payload.length);return signed.set(prefix),signed.set(payload,prefix.length),new Uint8Array(await crypto.subtle.sign("HMAC",key,signed))}function equalInConstantTime(a,b){let difference=0;for(let i=0;i<a.length;i++)difference|=(a[i]??0)^(b[i]??0);return difference===0}async function checkWebhookSignature(secret,headers,body,options={}){let id=header(headers,WEBHOOK_HEADERS.id),timestamp=header(headers,WEBHOOK_HEADERS.timestamp),signatures=header(headers,WEBHOOK_HEADERS.signature);if(!id||!timestamp||!signatures)return"missing_headers";if(!/^[0-9]{1,15}$/.test(timestamp))return"timestamp";let nowSeconds=(options.now??new Date).getTime()/1000,tolerance=options.toleranceSeconds??WEBHOOK_TOLERANCE_SECONDS;if(Math.abs(nowSeconds-Number(timestamp))>tolerance)return"timestamp";let expected=await mac(secret,id,timestamp,body);for(let candidate of signatures.split(" ",MAX_SIGNATURE_CANDIDATES)){let[version,value]=candidate.split(",",2);if(version!=="v1"||!value)continue;let given=fromBase64(value);if(given?.length===expected.length&&equalInConstantTime(given,expected))return null}return"signature"}async function verifyWebhook(secret,headers,body,options={}){return await checkWebhookSignature(secret,headers,body,options)===null}import{createORPCClient,ORPCError}from"@orpc/client";import{ResponseValidationPlugin}from"@orpc/contract/plugins";import{OpenAPILink}from"@orpc/openapi-client/fetch";var DEFAULT_BASE_URL="https://api.workwae.com";function parseRetryAfter(value){if(value===null)return null;let trimmed=value.trim();if(/^\d+(\.\d+)?$/.test(trimmed))return Math.round(Number(trimmed)*1000);let at=Date.parse(trimmed);return Number.isNaN(at)?null:Math.max(0,at-Date.now())}function sleep(ms,signal){return new Promise((resolve,reject)=>{if(signal?.aborted){reject(signal.reason);return}let onAbort=()=>{clearTimeout(timer),reject(signal?.reason)},timer=setTimeout(()=>{signal?.removeEventListener("abort",onAbort),resolve()},ms);signal?.addEventListener("abort",onAbort,{once:!0})})}var BACKOFF_START_MS=500,SEEN=Symbol("workwae.seen");function retrying(send,options={}){let maxRetries=options.maxRetries??2,maxDelayMs=options.maxDelayMs??60000,wait=options.wait??sleep;return async(request,init,{signal,context})=>{if(request.method==="POST"&&!request.headers.has("idempotency-key"))request.headers.set("idempotency-key",crypto.randomUUID());for(let attempt=0;;attempt++){let last=attempt>=maxRetries,response=await send(last?request:request.clone(),init),seen=context[SEEN];if(seen)seen.requestId=response.headers.get("x-request-id")??void 0;if(last||response.status!==429&&response.status!==503)return response;let retryAfterMs=parseRetryAfter(response.headers.get("retry-after"));if(retryAfterMs!==null&&retryAfterMs>maxDelayMs)return response;await response.body?.cancel();let backoff=BACKOFF_START_MS*2**attempt*(0.5+Math.random()/2);await wait(retryAfterMs??Math.min(backoff,maxDelayMs),signal),signal?.throwIfAborted()}}}function createClient(options){let{apiKey}=options,link=new OpenAPILink(v1Contract,{url:options.baseUrl??DEFAULT_BASE_URL,headers:async({context})=>{let key=typeof apiKey==="function"?await apiKey():apiKey;return{...options.headers,...key&&{authorization:`Bearer ${key}`},...context.idempotencyKey&&{"idempotency-key":context.idempotencyKey}}},fetch:retrying(options.fetch??((request,init)=>fetch(request,init)),options.retry),interceptors:[async({next,...call})=>{let seen={requestId:void 0};try{return await next({...call,context:{...call.context,[SEEN]:seen}})}catch(error){if(error instanceof ORPCError)error.requestId=seen.requestId;throw error}}],clientInterceptors:[async({next})=>{let response=await next();if(response.status>=400){let read=response.body;response.body=()=>read().catch(()=>{return})}return response}],plugins:[new ResponseValidationPlugin(v1Contract)]});return createORPCClient(link)}import{ORPCError as ORPCError2}from"@orpc/client";import{isDefinedError,ORPCError as ORPCError3,safe}from"@orpc/client";function isApiError(error,reason){return error instanceof ORPCError2&&isErrorReason(error.code)&&(reason===void 0||error.code===reason)}function pages(list,input,options){return followCursor((cursor)=>list({...input,cursor},options),input.cursor,(page)=>page.nextCursor??void 0)}async function*paginate(list,input,options){for await(let page of pages(list,input,options))yield*page.items}class ChangeFeed{list;input;options;#cursor;constructor(list,input,options){this.list=list;this.input=input;this.options=options;this.#cursor=input.cursor}get cursor(){return this.#cursor}async*[Symbol.asyncIterator](){let feed=followCursor((cursor)=>this.list({...this.input,cursor},this.options),this.#cursor,(page)=>page.items.length===0?void 0:page.nextCursor);for await(let page of feed)yield*page.items,this.#cursor=page.nextCursor}}function changes2(list,input,options){return new ChangeFeed(list,input,options)}import*as z49 from"zod";var LaterEvent=z49.looseObject({id:EventId,type:z49.string(),accountId:Id.nullable(),createdAt:Timestamp,data:z49.looseObject({}),isTest:z49.literal(!0).exactOptional()});async function parseWebhook(secret,headers,body,options={}){let failure=await checkWebhookSignature(secret,headers,body,options);if(failure)throw new WebhookVerificationError(failure);let json;try{json=JSON.parse(typeof body==="string"?body:new TextDecoder().decode(body))}catch{throw new WebhookVerificationError("payload","The webhook body is not JSON")}let type=typeof json==="object"&&json!==null&&"type"in json?json.type:void 0;if(EventType.safeParse(type).success){let parsed=Event.safeParse(json);if(parsed.success)return{...json,...parsed.data,known:!0}}else{let later=LaterEvent.safeParse(json);if(later.success)return{...later.data,known:!1}}throw new WebhookVerificationError("payload")}export{ChangeFeed,EVENT_TYPES,MAX_SIGNATURE_CANDIDATES,MAX_TEMPLATE_BODY_LENGTH,MAX_TEMPLATE_DESCRIPTION_LENGTH,MAX_TEMPLATE_NAME_LENGTH,MAX_TEMPLATE_PREVIEW_TEXT_LENGTH,MAX_TEMPLATE_SUBJECT_LENGTH,ORPCError3 as ORPCError,WEBHOOK_TOLERANCE_SECONDS,WebhookVerificationError,changes2 as changes,checkWebhookSignature,createClient,isApiError,isDefinedError,pages,paginate,parseWebhook,safe,verifyWebhook};
1
+ import*as z3 from"zod";var MAX_EMAIL_LENGTH=320,MAX_NAME_LENGTH=100,MAX_EXTERNAL_REF_LENGTH=255,MAX_DISPLAY_NAME_LENGTH=255,MAX_ADDRESS_NAME_LENGTH=256,MAX_SECRET_LENGTH=1024,MAX_NOTE_LENGTH=500,MAX_FILTER_QUERY_LENGTH=200,MAX_URL_LENGTH=2048,MAX_TIME_ZONE_LENGTH=64,MAX_CURSOR_LENGTH=512,MAX_PAGE_SIZE=200,DEFAULT_PAGE_SIZE=50,MAX_PROVIDER_ID_LENGTH=1024,MAX_HOST_LENGTH=253,MAX_PORT=65535,MAX_STATE_LENGTH=1024,MIN_TOKEN_TTL_SECONDS=60,MAX_TOKEN_TTL_SECONDS=86400,MAX_MCP_KEYS_PER_ACCOUNT=25,MAX_REDIRECT_URIS=50,MAX_UNDO_SEND_SECONDS=60,MIN_EVENT_RETENTION_DAYS=7,MAX_EVENT_RETENTION_DAYS=30,MAX_MCP_ALLOWED_DOMAINS=100,MAX_MCP_DAILY_SEND_LIMIT=1e4;var MAX_SUBJECT_LENGTH=998,MAX_MESSAGE_ID_LENGTH=998,MAX_BODY_LENGTH=2097152,MAX_RECIPIENTS=500,MAX_REPLY_TO=10,MAX_ATTACHMENTS=50,MAX_FILENAME_LENGTH=255,MAX_CONTENT_TYPE_LENGTH=255,MAX_CONTENT_ID_LENGTH=255,MAX_SEND_REFERENCE_LENGTH=255,MAX_SEND_WAIT_SECONDS=30,MAX_SEND_HEADERS=50,MAX_HEADER_VALUE_LENGTH=4096,MAX_CALENDAR_LENGTH=262144,MAX_MAILBOX_CHANGES=50,MAX_SEARCH_QUERY_LENGTH=500,MAX_SEARCH_PAGE_SIZE=100,DEFAULT_SEARCH_PAGE_SIZE=25,MAX_THREAD_PAGE_SIZE=100,DEFAULT_THREAD_PAGE_SIZE=25,MAX_MESSAGE_BODIES_PAGE_SIZE=25;var MAX_SIGNATURE_HTML_LENGTH=1e5,MAX_SIGNATURE_TEXT_LENGTH=20000,MAX_TEMPLATE_NAME_LENGTH=255,MAX_TEMPLATE_DESCRIPTION_LENGTH=1000,MAX_TEMPLATE_SUBJECT_LENGTH=2000,MAX_TEMPLATE_PREVIEW_TEXT_LENGTH=2000,MAX_TEMPLATE_BODY_LENGTH=1e6,MAX_MAIL_MERGE_ENTRIES=100,MAX_MAIL_MERGE_ATTACHMENT_BYTES=52428800,MAX_RENDER_PARAMS_BYTES=262144,MAX_RENDER_ITERATIONS=1e4,MAX_MARKDOWN_LENGTH=1e5,MAX_SUPPRESSION_BATCH=1000,MAX_SUPPRESSION_LIST_DESCRIPTION_LENGTH=500,MAX_SUPPRESSION_REASON_LENGTH=200,MAX_EVENT_ATTENDEES=500,MAX_EVENT_SUBJECT_LENGTH=255,MAX_EVENT_LOCATION_LENGTH=255,MAX_EVENT_BODY_LENGTH=1e6,MAX_EVENT_COMMENT_LENGTH=2000,MAX_RECURRENCE_LINES=20,MAX_RECURRENCE_LINE_LENGTH=1000,MAX_REMINDER_MINUTES=40320,MAX_AVAILABILITY_ATTENDEES=20,MAX_AVAILABILITY_DAYS=62,MIN_SLOT_MINUTES=5,MAX_SLOT_MINUTES=1440,MAX_SUGGESTIONS=100,MAX_AVAILABILITY_ACCOUNTS=20,MAX_EVENT_RANGE_DAYS=400,MAX_CALENDAR_EVENT_PAGE_SIZE=500,DEFAULT_CALENDAR_EVENT_PAGE_SIZE=100,MAX_SCHEDULING_DAY_WINDOWS=8,MAX_SCHEDULING_WINDOW_DAYS=365,MAX_SCHEDULING_BUFFER_MINUTES=1440,MAX_SCHEDULING_NOTICE_MINUTES=86400,MAX_SCHEDULING_PER_DAY=1000,MAX_SCHEDULING_TITLE_LENGTH=255,MAX_SCHEDULING_DESCRIPTION_LENGTH=1e4,MAX_BOOKING_NOTES_LENGTH=2000,MAX_SCHEDULING_SLOTS=1000,DEFAULT_SCHEDULING_SLOTS=200,MAX_CONTACT_ENTRIES=20,MAX_CONTACT_FIELD_LENGTH=256,MAX_CONTACT_PHONE_LENGTH=64,MAX_CONTACT_NOTES_LENGTH=1e4,MAX_CONTACT_PAGE_SIZE=500,DEFAULT_CONTACT_PAGE_SIZE=100,MAX_WEBHOOK_FILTER_VALUES=100,MAX_SUBJECT_FILTER_LENGTH=200,MAX_REPLAY_EVENTS=1000,MAX_WEBHOOK_ATTEMPT_PAGE_SIZE=500,DEFAULT_WEBHOOK_ATTEMPT_PAGE_SIZE=50,MAX_SIGNATURE_CANDIDATES=8,DEFAULT_WEBHOOK_SECRET_GRACE_SECONDS=86400,MAX_WEBHOOK_SECRET_GRACE_SECONDS=604800,MAX_DRAFT_INSTRUCTIONS_LENGTH=2000,MAX_DRAFT_CONTEXT_LENGTH=20000,MAX_EMAIL_VALIDATIONS=10,MAX_MEETING_APP_ID_LENGTH=200,MAX_REFRESH_TOKEN_LENGTH=4096,MAX_KEY_FILE_LENGTH=20000,MAX_DIRECTORY_QUERY_LENGTH=100,MAX_DIRECTORY_PAGE_SIZE=200,DEFAULT_DIRECTORY_PAGE_SIZE=100,MAX_ADD_MAILBOXES=100,MAX_ENTRA_ID_LENGTH=100;var MAX_VALIDATION_CREDITS=1e9;var MAX_CREDIT_PACK_CREDITS=1e7,MAX_CREDIT_PACKS_PER_PLAN=5;var MAX_USAGE_RANGE_DAYS=366;import*as z from"zod";var openApiComponents=new Map,componentNames=new WeakMap;function named(name,schema){if(openApiComponents.has(name))throw Error(`OpenAPI component ${name} registered twice`);return openApiComponents.set(name,schema),componentNames.set(schema,name),schema}function componentName(schema,wrapper){let name=componentNames.get(schema);if(name===void 0)throw Error(`${wrapper}() of a schema that is not named()`);return name}var Id=z.uuid(),ProviderId=z.string().min(1).max(MAX_PROVIDER_ID_LENGTH),EventId=named("EventId",z.string().regex(/^[0-9]{1,19}$/,"Expected an event id")),Cursor=z.string().min(1).max(MAX_CURSOR_LENGTH),Email=z.email().max(MAX_EMAIL_LENGTH),Timestamp=z.preprocess((value)=>typeof value==="string"?new Date(value):value,z.date()),QueryTimestamp=z.preprocess((value)=>typeof value==="string"?new Date(value):value,z.date()),DateOnly=z.iso.date();function trimmed(max){return z.string().trim().min(1).max(max)}var Name=z.string().min(1).max(MAX_NAME_LENGTH),ExternalRef=z.string().max(MAX_EXTERNAL_REF_LENGTH),Address=named("Address",z.object({name:z.string().nullable(),address:z.string()})),AddressInput=named("AddressInput",z.object({name:z.string().max(MAX_ADDRESS_NAME_LENGTH).optional(),address:Email})),QueryBoolean=z.union([z.boolean(),z.enum(["true","false"])]).transform((v)=>v===!0||v==="true"),pageFields=(item)=>({items:z.array(item),nextCursor:Cursor.nullable()}),Page=(item)=>named(`${componentName(item,"Page")}Page`,z.object(pageFields(item)));async function*followCursor(fetch2,cursor,next){let at=cursor;for(;;){let page=await fetch2(at);if(yield page,at=next(page),at===void 0)return}}var listFields=(item)=>({items:z.array(item)}),List=(item)=>named(`${componentName(item,"List")}List`,z.object(listFields(item)));var DAY_MS=86400000;function instant(value){if(value instanceof Date)return value.getTime();if(typeof value!=="string")return null;let ms=Date.parse(value);return Number.isNaN(ms)?null:ms}function withDateRange(schema,range){return schema.superRefine((value,ctx)=>{let from=instant(value[range.from]),to=instant(value[range.to]);if(from===null||to===null)return;let span=to-from,params={reason:"invalid_range"};if(span<0||span===0&&!range.inclusive)ctx.addIssue({code:"custom",path:[range.to],message:`${range.to} must be ${range.inclusive?"on or after":"after"} ${range.from}`,params});else if(range.maxSpanMs!==void 0&&span>range.maxSpanMs)ctx.addIssue({code:"custom",path:[range.to],message:range.maxSpanMs%DAY_MS===0?`The range may span at most ${range.maxSpanMs/DAY_MS} days`:`The range may span at most ${range.maxSpanMs} ms`,params})})}function pageLimit(max,fallback){return z.coerce.number().int().min(1).max(max).default(fallback)}var Limit=pageLimit(MAX_PAGE_SIZE,DEFAULT_PAGE_SIZE),Paged={limit:Limit,cursor:Cursor.optional()},AccountIdInput=named("AccountIdInput",z.object({accountId:Id})),StreamedFile=z.custom((value)=>value instanceof ReadableStream||value instanceof Blob).transform((stream)=>stream);var publicOperation=(operation)=>({...operation,security:[]});function fileResponse(file){return(operation)=>{let ok=operation.responses?.["200"];if(!ok||"$ref"in ok)throw Error(`OpenAPI: no 200 response for ${operation.operationId}`);return{...operation,responses:{...operation.responses,200:{...ok,description:file.description,content:{[file.mediaType]:{schema:{type:"string",contentMediaType:file.mediaType}}},headers:{"content-disposition":{description:`Always \`${file.disposition}; filename="..."\``,schema:{type:"string"}},...file.headers}}}}}}import*as z2 from"zod";var BodyStorage=named("BodyStorage",z2.enum(["none","cache","full"])),EffectiveSettings=named("EffectiveSettings",z2.object({bodyStorage:BodyStorage,syncWindowDays:z2.number().int().positive().nullable(),mailEnabled:z2.boolean(),calendarEnabled:z2.boolean(),contactsEnabled:z2.boolean(),undoSendSeconds:z2.number().int().min(0).max(MAX_UNDO_SEND_SECONDS),trackOpens:z2.boolean(),trackClicks:z2.boolean(),trackingHoneypot:z2.boolean(),suppressionCheck:z2.enum(["lists","always"]),sendsPerMinute:z2.number().int().min(1).max(600).nullable()})),SettingsOverride=named("SettingsOverride",EffectiveSettings.partial());var OptOutHandling=named("OptOutHandling",z2.enum(["off","review","suppress"])),McpSendMode=named("McpSendMode",z2.enum(["allow","confirm","drafts_only"])),RecipientDomain=z2.string().trim().toLowerCase().regex(/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9-]{2,63}$/,'Expected a domain name such as "example.com"'),McpSendPolicy=named("McpSendPolicy",z2.object({mode:McpSendMode,allowedRecipientDomains:z2.array(RecipientDomain).min(1).max(MAX_MCP_ALLOWED_DOMAINS).nullable(),dailySendLimit:z2.number().int().min(1).max(MAX_MCP_DAILY_SEND_LIMIT).nullable(),externalRecipientConfirmation:z2.boolean()})),McpSendPolicyPatch=named("McpSendPolicyPatch",McpSendPolicy.partial()),TenantSettings=named("TenantSettings",EffectiveSettings.extend({optOutHandling:OptOutHandling,eventRetentionDays:z2.number().int().min(MIN_EVENT_RETENTION_DAYS).max(MAX_EVENT_RETENTION_DAYS),redirectUris:z2.array(z2.url()).max(MAX_REDIRECT_URIS),aiReplyClassification:z2.boolean(),mcpSendPolicy:McpSendPolicy})),UpdateTenantSettings=named("UpdateTenantSettings",TenantSettings.partial().extend({mcpSendPolicy:McpSendPolicyPatch.optional()}));var OAuthProvider=named("OAuthProvider",z3.enum(["google","microsoft"])),MicrosoftCloud=named("MicrosoftCloud",z3.enum(["global","gcc-high","dod","china"])),AccountProvider=named("AccountProvider",z3.enum(["gmail","graph","imap"])),AccountStatus=named("AccountStatus",z3.enum(["active","paused","reauth_required","disabled"])),SettableAccountStatus=named("SettableAccountStatus",AccountStatus.extract(["active","paused"])),RuntimeState=named("RuntimeState",z3.enum(["pending","connecting","connected","error","stopped"])),AccountRuntime=named("AccountRuntime",z3.object({state:RuntimeState,detail:z3.string().nullable(),ownerInstanceId:Id.nullable(),updatedAt:Timestamp})),MailSecurity=named("MailSecurity",z3.enum(["tls","starttls","none"])),MailServer=named("MailServer",z3.object({host:z3.string(),port:z3.number().int(),security:MailSecurity,user:z3.string()})),DavAuth=named("DavAuth",z3.enum(["imap_password","dav_password"])),DavSettings=named("DavSettings",z3.object({calendarUrl:z3.string().nullable(),contactsUrl:z3.string().nullable(),user:z3.string(),auth:DavAuth})),ImapAuth=named("ImapAuth",z3.enum(["password","google","microsoft"])),ImapSettings=named("ImapSettings",z3.object({imap:MailServer,smtp:MailServer.nullable(),auth:ImapAuth,appendSent:z3.boolean(),dav:DavSettings.nullable()})),AccountAuthMethod=named("AccountAuthMethod",z3.enum(["oauth","password","google_delegation","microsoft_app","shared_mailbox","google_group"])),MailPermissions=named("MailPermissions",z3.enum(["full","read_send","read"])),AccountAuth=named("AccountAuth",z3.object({method:AccountAuthMethod,orgConnectionId:Id.nullable(),delegateAccountId:Id.nullable(),relayEmail:Email.nullable()})),Account=named("Account",z3.object({id:Id,externalRef:z3.string().nullable(),provider:AccountProvider,email:z3.string(),displayName:z3.string().nullable(),oauthAppId:Id.nullable(),cloud:MicrosoftCloud.nullable(),status:AccountStatus,statusReason:z3.string().nullable(),settings:EffectiveSettings,settingsOverride:SettingsOverride,runtime:AccountRuntime.nullable(),imap:ImapSettings.nullable(),auth:AccountAuth,mailPermissions:MailPermissions.nullable(),initializedAt:Timestamp.nullable(),calendarInitializedAt:Timestamp.nullable(),contactsInitializedAt:Timestamp.nullable(),createdAt:Timestamp,updatedAt:Timestamp})),SyncKind=named("SyncKind",z3.enum(["mail","calendar","contacts"])),SyncAccountRequest=named("SyncAccountRequest",z3.object({kinds:z3.array(SyncKind).min(1).optional()})),AccountSyncQueued=named("AccountSyncQueued",z3.object({kinds:z3.array(SyncKind)})),AccountPage=Page(Account),ListAccountsQuery=named("ListAccountsQuery",z3.object({...Paged,externalRef:z3.string().optional(),q:trimmed(MAX_FILTER_QUERY_LENGTH).optional(),status:AccountStatus.optional(),orgConnectionId:Id.optional(),delegateAccountId:Id.optional()})),CreateAccount=named("CreateAccount",z3.object({provider:OAuthProvider,microsoftCloud:MicrosoftCloud.default("global"),refreshToken:z3.string().min(1),oauthAppId:Id.optional(),externalRef:ExternalRef.optional(),settings:SettingsOverride.optional(),sharedMailboxesEnabled:z3.boolean().optional(),mailPermissions:MailPermissions.optional()})),UpdateAccount=named("UpdateAccount",z3.object({externalRef:ExternalRef.nullable().optional(),status:SettableAccountStatus.optional(),settings:SettingsOverride.optional()})),DeleteAccountQuery=named("DeleteAccountQuery",z3.object({purge:QueryBoolean.default(!1)})),DeleteAccountInput=named("DeleteAccountInput",z3.object({params:AccountIdInput,query:DeleteAccountQuery})),MintAccountTokenRequest=named("MintAccountTokenRequest",z3.object({ttlSeconds:z3.number().int().min(MIN_TOKEN_TTL_SECONDS).max(MAX_TOKEN_TTL_SECONDS).default(900)})),AccountToken=named("AccountToken",z3.object({token:z3.string(),expiresAt:Timestamp})),MailServerInput=named("MailServerInput",z3.object({host:z3.string().min(1).max(MAX_HOST_LENGTH),port:z3.number().int().min(1).max(MAX_PORT),security:MailSecurity,user:z3.string().min(1).max(MAX_EMAIL_LENGTH).optional()})),Password=z3.string().min(1).max(MAX_SECRET_LENGTH),DavUrl=z3.url({protocol:/^https?$/}).max(MAX_URL_LENGTH),DavInput=named("DavInput",z3.object({calendarUrl:DavUrl.nullable().optional(),contactsUrl:DavUrl.nullable().optional(),user:z3.string().min(1).max(MAX_EMAIL_LENGTH).optional(),password:Password.nullable().optional()})),ImapAccountInput=named("ImapAccountInput",z3.object({email:Email,displayName:z3.string().max(MAX_DISPLAY_NAME_LENGTH).optional(),password:Password,smtpPassword:Password.optional(),imap:MailServerInput.optional(),smtp:MailServerInput.nullable().optional(),appendSent:z3.boolean().optional(),dav:DavInput.optional()})),CreateImapAccount=named("CreateImapAccount",ImapAccountInput.extend({externalRef:ExternalRef.optional(),settings:SettingsOverride.optional()})),UpdateImapSettings=named("UpdateImapSettings",z3.object({password:Password.optional(),smtpPassword:Password.nullable().optional(),imap:MailServerInput.optional(),smtp:MailServerInput.nullable().optional(),appendSent:z3.boolean().optional(),dav:DavInput.nullable().optional()})),DavDiscoverySource=named("DavDiscoverySource",z3.enum(["provider","well-known","srv"])),DiscoveredDav=named("DiscoveredDav",z3.object({source:DavDiscoverySource,calendarUrl:z3.string().nullable(),contactsUrl:z3.string().nullable()})),AutoconfigSource=named("AutoconfigSource",z3.enum(["provider","autoconfig","ispdb","srv","mx"])),DiscoveredSettings=named("DiscoveredSettings",z3.object({source:AutoconfigSource,imap:MailServer,smtp:MailServer.nullable(),oauth:OAuthProvider.nullable(),dav:DiscoveredDav.nullable()})),AutoconfigQuery=named("AutoconfigQuery",z3.object({email:Email}));import*as z4 from"zod";var AiDraftFormat=named("AiDraftFormat",z4.enum(["text","markdown"])),AiDraftContext=named("AiDraftContext",z4.object({accountId:Id.optional(),replyToMessageId:Id.optional(),currentDraft:z4.string().max(MAX_DRAFT_CONTEXT_LENGTH).optional()}).refine((c)=>!c.replyToMessageId||c.accountId,{message:"replyToMessageId needs accountId",path:["accountId"]})),CreateAiDraft=named("CreateAiDraft",z4.object({instructions:trimmed(MAX_DRAFT_INSTRUCTIONS_LENGTH),context:AiDraftContext.optional(),format:AiDraftFormat})),AiDraft=named("AiDraft",z4.object({text:z4.string()}));import*as z5 from"zod";var AuditActorType=named("AuditActorType",z5.enum(["user","apiKey","accountToken","connectSession","scimCredential"])),AuditActor=named("AuditActor",z5.object({type:AuditActorType,id:Id,label:z5.string().nullable()})),AuditSurface=named("AuditSurface",z5.enum(["v1","rpc","mcp","auth","smtp"])),AuditOutcome=named("AuditOutcome",z5.enum(["success","failure"])),AuditEntry=named("AuditEntry",z5.object({id:Id,createdAt:Timestamp,actor:AuditActor,action:z5.string(),surface:AuditSurface,targets:z5.record(z5.string(),z5.union([z5.string(),z5.array(z5.string())])),ip:z5.string().nullable(),userAgent:z5.string().nullable(),status:z5.number().int(),errorReason:z5.string().nullable(),requestId:z5.string().nullable()})),AuditEntryPage=Page(AuditEntry),ListAuditLogQuery=named("ListAuditLogQuery",withDateRange(z5.object({...Paged,actorType:AuditActorType.optional(),actorId:Id.optional(),action:z5.string().min(1).max(MAX_FILTER_QUERY_LENGTH).optional(),accountId:Id.optional(),outcome:AuditOutcome.optional(),from:QueryTimestamp.optional(),to:QueryTimestamp.optional()}),{from:"from",to:"to",inclusive:!0}));import*as z9 from"zod";import*as z8 from"zod";import*as z7 from"zod";import*as z6 from"zod";var McpKeyScope=named("McpKeyScope",z6.enum(["read","write","send"])),McpKeyScopes=z6.array(McpKeyScope).min(1),McpKey=named("McpKey",z6.object({id:Id,name:z6.string(),prefix:z6.string(),scopes:z6.array(McpKeyScope),accountId:Id,expiresAt:Timestamp.nullable(),createdAt:Timestamp,lastUsedAt:Timestamp.nullable(),sendPolicy:McpSendPolicyPatch.nullable()})),McpKeyList=List(McpKey),CreatedMcpKey=named("CreatedMcpKey",McpKey.extend({key:z6.string(),mcpUrl:z6.url()})),CreateMcpKey=named("CreateMcpKey",z6.object({name:Name.default("MCP"),scopes:McpKeyScopes,expiresAt:Timestamp.refine((at)=>at.getTime()>Date.now(),{message:"expiresAt must be in the future"}).nullable().optional(),sendPolicy:McpSendPolicyPatch.nullable().optional()})),McpKeyIdInput=named("McpKeyIdInput",AccountIdInput.extend({keyId:Id})),ConnectSessionMcp=named("ConnectSessionMcp",z6.object({scopes:McpKeyScopes,name:Name.default("MCP")}));var MailAccess=named("MailAccess",z7.enum(["api","imap"])),ConnectOptions=named("ConnectOptions",z7.object({mailEnabled:z7.boolean().optional(),calendarEnabled:z7.boolean().optional(),contactsEnabled:z7.boolean().optional(),bodyStorage:BodyStorage.optional(),mailAccess:MailAccess.optional(),sharedMailboxesEnabled:z7.boolean().optional(),mailPermissions:MailPermissions.optional()})),ConnectProvider=named("ConnectProvider",z7.enum(["google","microsoft","imap"])),ConnectSessionStatus=named("ConnectSessionStatus",z7.enum(["pending","completed","failed"])),ConnectErrorCode=named("ConnectErrorCode",z7.enum(["session_used","session_expired","provider_not_allowed","access_denied","access_token_rejected","provider_error","provider_unavailable","provider_not_configured","unsupported_provider","token_exchange_failed","invalid_client","interaction_required","invalid_grant","missing_refresh_token","scope_denied","identity_failed","identity_unavailable","credential_conflict","credential_missing","delegate_missing","account_exists","account_mismatch","account_not_found","unknown_oauth_app","oauth_app_mismatch","oauth_app_secret_unreadable","google_imap_requires_own_app","unknown_meeting_app","meeting_app_mismatch","meeting_app_secret_unreadable","connect_domain_inactive","consent_failed","consent_required","consent_revoked","consent_unverified","directory_already_connected","directory_mismatch","directory_unavailable","invalid_certificate","invalid_service_account_key","org_connection_not_found","org_connection_secret_unreadable","plan_limit_accounts","plan_required","spend_cap_reached","plan_module_unavailable","connect_grants_nothing","billing_past_due","internal_error"])),ConnectSessionPublic=named("ConnectSessionPublic",z7.object({tenantName:z7.string(),providers:z7.array(ConnectProvider),microsoftCloud:MicrosoftCloud,status:ConnectSessionStatus,reauthEmail:z7.string().nullable(),modules:z7.object({mail:z7.boolean(),calendar:z7.boolean(),contacts:z7.boolean()}),loginHint:z7.string().nullable(),expiresAt:Timestamp})),ConnectSession=named("ConnectSession",z7.object({id:Id,status:ConnectSessionStatus,error:ConnectErrorCode.nullable(),accountId:Id.nullable(),email:z7.string().nullable(),mcpKeyId:Id.nullable(),mcpKeySkipped:z7.enum(["mcp_key_limit"]).nullable(),expiresAt:Timestamp,createdAt:Timestamp})),RedirectState=z7.string().max(MAX_STATE_LENGTH),SessionTtlSeconds=z7.number().int().min(MIN_TOKEN_TTL_SECONDS).max(MAX_TOKEN_TTL_SECONDS).default(3600),CreateConnectSession=named("CreateConnectSession",z7.object({redirectUri:z7.url().optional(),providers:z7.array(ConnectProvider).min(1).default(["google","microsoft"]),microsoftCloud:MicrosoftCloud.default("global"),oauthAppIds:z7.object({google:Id.optional(),microsoft:Id.optional()}).optional(),externalRef:ExternalRef.optional(),loginHint:Email.optional(),state:RedirectState.optional(),accountId:Id.optional(),options:ConnectOptions.optional(),mcp:ConnectSessionMcp.optional(),ttlSeconds:SessionTtlSeconds})),SessionLink=named("SessionLink",z7.object({id:Id,url:z7.url(),expiresAt:Timestamp})),ConnectSessionIdInput=named("ConnectSessionIdInput",z7.object({sessionId:Id})),StartOAuthRequest=named("StartOAuthRequest",z7.object({provider:OAuthProvider,redirectUri:z7.url(),microsoftCloud:MicrosoftCloud.default("global"),oauthAppId:Id.optional(),externalRef:ExternalRef.optional(),state:RedirectState.optional(),accountId:Id.optional(),options:ConnectOptions.optional()})),OAuthAuthorization=named("OAuthAuthorization",z7.object({authorizeUrl:z7.url()})),SessionTokenInput=named("SessionTokenInput",z7.object({token:z7.string().min(1)})),HostedImapResult=named("HostedImapResult",z7.object({redirectUrl:z7.url()}));var MeetingService=named("MeetingService",z8.enum(["zoom","webex"])),MeetingConnectionStatus=named("MeetingConnectionStatus",z8.enum(["active","reauth_required"])),MeetingConnection=named("MeetingConnection",z8.object({service:MeetingService,email:z8.string(),displayName:z8.string().nullable(),meetingAppId:Id.nullable(),status:MeetingConnectionStatus,statusReason:z8.string().nullable(),createdAt:Timestamp,updatedAt:Timestamp})),MeetingConnectionList=List(MeetingConnection),MeetingAppKind=named("MeetingAppKind",z8.enum(["zoom_oauth","zoom_s2s","webex_oauth","webex_service_app"])),MeetingAppStatus=named("MeetingAppStatus",z8.enum(["unverified","active","failing"])),MeetingApp=named("MeetingApp",z8.object({id:Id,kind:MeetingAppKind,service:MeetingService,name:z8.string(),clientId:z8.string(),zoomAccountId:z8.string().nullable(),status:MeetingAppStatus.nullable(),statusDetail:z8.string().nullable(),checkedAt:Timestamp.nullable(),scopes:z8.array(z8.string()),useConnectDomain:z8.boolean(),redirectUri:z8.string().nullable(),createdAt:Timestamp})),MeetingAppList=List(MeetingApp),ClientId=z8.string().min(1).max(MAX_MEETING_APP_ID_LENGTH),ClientSecret=z8.string().min(1).max(MAX_SECRET_LENGTH),UseConnectDomain=z8.boolean().optional(),CreateMeetingApp=named("CreateMeetingApp",z8.discriminatedUnion("kind",[z8.object({kind:z8.literal("zoom_oauth"),name:Name,clientId:ClientId,clientSecret:ClientSecret,useConnectDomain:UseConnectDomain}),z8.object({kind:z8.literal("zoom_s2s"),name:Name,accountId:z8.string().min(1).max(MAX_MEETING_APP_ID_LENGTH),clientId:ClientId,clientSecret:ClientSecret,useConnectDomain:UseConnectDomain}),z8.object({kind:z8.literal("webex_oauth"),name:Name,clientId:ClientId,clientSecret:ClientSecret,useConnectDomain:UseConnectDomain}),z8.object({kind:z8.literal("webex_service_app"),name:Name,clientId:ClientId,clientSecret:ClientSecret,useConnectDomain:UseConnectDomain,refreshToken:z8.string().min(1).max(MAX_REFRESH_TOKEN_LENGTH)})])),UpdateMeetingApp=named("UpdateMeetingApp",z8.object({useConnectDomain:z8.boolean().optional()})),MeetingAppCheck=named("MeetingAppCheck",z8.object({name:z8.string(),isOk:z8.boolean(),detail:z8.string()})),MeetingAppTestResult=named("MeetingAppTestResult",z8.object({app:MeetingApp,checks:z8.array(MeetingAppCheck)})),MeetingConnectSessionPublic=named("MeetingConnectSessionPublic",z8.object({tenantName:z8.string(),service:MeetingService,accountEmail:z8.string(),status:ConnectSessionStatus,expiresAt:Timestamp})),MeetingAppIdInput=named("MeetingAppIdInput",z8.object({appId:Id})),TestMeetingAppRequest=named("TestMeetingAppRequest",z8.object({user:Email.optional()})),MeetingConnectionInput=named("MeetingConnectionInput",AccountIdInput.extend({service:MeetingService})),CreateMeetingConnectSession=named("CreateMeetingConnectSession",z8.object({service:MeetingService,redirectUri:z8.url().optional(),meetingAppId:Id.optional(),state:RedirectState.optional(),ttlSeconds:SessionTtlSeconds}));var CalendarAccess=named("CalendarAccess",z9.enum(["owner","writer","reader","freebusy"])),Calendar=named("Calendar",z9.object({id:Id,name:z9.string(),description:z9.string().nullable(),owner:Address.nullable(),isPrimary:z9.boolean(),access:CalendarAccess,canEdit:z9.boolean(),color:z9.string().nullable(),timeZone:z9.string().nullable(),isHidden:z9.boolean()})),CalendarList=List(Calendar),CalendarColor=z9.string().regex(/^#[0-9a-fA-F]{6}$/),CalendarIdInput=named("CalendarIdInput",AccountIdInput.extend({calendarId:Id})),CreateCalendar=named("CreateCalendar",z9.object({name:trimmed(MAX_NAME_LENGTH),description:trimmed(MAX_EVENT_BODY_LENGTH).nullable().optional(),color:CalendarColor.nullable().optional(),timeZone:trimmed(MAX_TIME_ZONE_LENGTH).nullable().optional()})),CreateCalendarInput=named("CreateCalendarInput",AccountIdInput.extend(CreateCalendar.shape)),UpdateCalendar=named("UpdateCalendar",z9.object({name:trimmed(MAX_NAME_LENGTH).optional(),description:trimmed(MAX_EVENT_BODY_LENGTH).nullable().optional(),color:CalendarColor.nullable().optional(),timeZone:trimmed(MAX_TIME_ZONE_LENGTH).nullable().optional()})),CalendarChangedField=named("CalendarChangedField",Calendar.keyof().exclude(["id"])),EventKind=named("EventKind",z9.enum(["single","series","occurrence","exception"])),AttendeeType=named("AttendeeType",z9.enum(["required","optional","resource"])),ResponseStatus=named("ResponseStatus",z9.enum(["none","organizer","accepted","tentative","declined","needs_action"])),EventStatus=named("EventStatus",z9.enum(["confirmed","tentative","cancelled"])),ShowAs=named("ShowAs",z9.enum(["free","busy","tentative","oof","working_elsewhere","unknown"])),Sensitivity=named("Sensitivity",z9.enum(["normal","personal","private","confidential"])),Attendee=named("Attendee",Address.extend({type:AttendeeType,response:ResponseStatus})),EventTime=named("EventTime",z9.object({dateTime:Timestamp,date:DateOnly.nullable(),timeZone:z9.string().nullable()})),CalendarEvent=named("CalendarEvent",z9.object({id:Id,calendarId:Id,kind:EventKind,seriesId:Id.nullable(),originalStart:Timestamp.nullable(),subject:z9.string().nullable(),snippet:z9.string().nullable(),location:z9.string().nullable(),start:EventTime,end:EventTime,isAllDay:z9.boolean(),organizer:Address.nullable(),attendees:z9.array(Attendee),isOrganizer:z9.boolean(),selfResponse:ResponseStatus,status:EventStatus,showAs:ShowAs,sensitivity:Sensitivity,onlineMeetingUrl:z9.string().nullable(),onlineMeetingProvider:z9.string().nullable(),iCalUid:z9.string().nullable(),recurrence:z9.array(z9.string()).nullable(),recurrenceTimeZone:z9.string().nullable(),cancelledStarts:z9.array(Timestamp).nullable(),categories:z9.array(z9.string()),hasAttachments:z9.boolean(),reminderMinutes:z9.number().int().nullable(),webLink:z9.string().nullable(),updatedAt:Timestamp})),CalendarEventPage=Page(CalendarEvent),CalendarEventChangedField=named("CalendarEventChangedField",CalendarEvent.keyof().exclude(["id","calendarId","seriesId","updatedAt"])),BodyContentType=named("BodyContentType",z9.enum(["text","html"])),EventBody=named("EventBody",z9.object({contentType:BodyContentType,content:z9.string()})),CalendarEventDetail=named("CalendarEventDetail",CalendarEvent.extend({body:EventBody.nullable()})),EventTimeInput=named("EventTimeInput",z9.union([z9.object({dateTime:z9.iso.datetime({local:!0}),timeZone:z9.string().min(1).max(MAX_TIME_ZONE_LENGTH)}),z9.object({date:DateOnly})])),AttendeeInput=named("AttendeeInput",AddressInput.extend({type:AttendeeType.default("required")})),RecurrenceLine=z9.string().max(MAX_RECURRENCE_LINE_LENGTH).regex(/^(RRULE|RDATE|EXDATE)[:;]/,"Expected an RRULE, RDATE or EXDATE line"),EventFields={subject:z9.string().max(MAX_EVENT_SUBJECT_LENGTH),body:EventBody.extend({content:z9.string().max(MAX_EVENT_BODY_LENGTH)}),location:z9.string().max(MAX_EVENT_LOCATION_LENGTH),start:EventTimeInput,end:EventTimeInput,attendees:z9.array(AttendeeInput).max(MAX_EVENT_ATTENDEES),recurrence:z9.array(RecurrenceLine).min(1).max(MAX_RECURRENCE_LINES),showAs:ShowAs.exclude(["unknown"]),sensitivity:Sensitivity,reminderMinutes:z9.number().int().min(0).max(MAX_REMINDER_MINUTES).nullable(),onlineMeetingUrl:z9.url({protocol:/^https$/}).max(MAX_URL_LENGTH)},Notify=z9.boolean().default(!0),CreateCalendarEvent=named("CreateCalendarEvent",z9.object({calendarId:Id,subject:EventFields.subject.optional(),body:EventFields.body.optional(),location:EventFields.location.optional(),start:EventFields.start,end:EventFields.end,attendees:EventFields.attendees.default([]),recurrence:EventFields.recurrence.optional(),showAs:EventFields.showAs.optional(),sensitivity:EventFields.sensitivity.optional(),reminderMinutes:EventFields.reminderMinutes.optional(),onlineMeeting:z9.boolean().default(!1),onlineMeetingUrl:EventFields.onlineMeetingUrl.optional(),onlineMeetingService:MeetingService.optional(),notify:Notify})),CreateCalendarEventInput=AccountIdInput.extend(CreateCalendarEvent.shape).superRefine((input,ctx)=>{if([input.onlineMeeting,input.onlineMeetingUrl!==void 0,input.onlineMeetingService!==void 0].filter(Boolean).length>1)ctx.addIssue({code:"custom",path:["onlineMeetingService"],message:"Give one of onlineMeeting, onlineMeetingUrl and onlineMeetingService",params:{reason:"multiple_meeting_options"}})}),UpdateCalendarEvent=named("UpdateCalendarEvent",z9.object({subject:EventFields.subject.optional(),body:EventFields.body.optional(),location:EventFields.location.optional(),start:EventFields.start.optional(),end:EventFields.end.optional(),attendees:EventFields.attendees.optional(),recurrence:EventFields.recurrence.optional(),showAs:EventFields.showAs.optional(),sensitivity:EventFields.sensitivity.optional(),reminderMinutes:EventFields.reminderMinutes.optional(),onlineMeetingUrl:EventFields.onlineMeetingUrl.nullable().optional(),notify:Notify})),DeleteCalendarEventQuery=named("DeleteCalendarEventQuery",z9.object({notify:QueryBoolean.default(!0),comment:z9.string().max(MAX_EVENT_COMMENT_LENGTH).optional()})),RsvpResponse=named("RsvpResponse",z9.enum(["accepted","tentative","declined"])),RespondToCalendarEventRequest=named("RespondToCalendarEventRequest",z9.object({response:RsvpResponse,comment:z9.string().max(MAX_EVENT_COMMENT_LENGTH).optional(),notify:z9.boolean().default(!0)})),CalendarEventIdInput=named("CalendarEventIdInput",AccountIdInput.extend({eventId:Id})),DeleteCalendarEventInput=named("DeleteCalendarEventInput",z9.object({params:CalendarEventIdInput,query:DeleteCalendarEventQuery})),ListCalendarEventsQuery=named("ListCalendarEventsQuery",z9.object({start:QueryTimestamp,end:QueryTimestamp,calendarId:Id.optional(),limit:pageLimit(MAX_CALENDAR_EVENT_PAGE_SIZE,DEFAULT_CALENDAR_EVENT_PAGE_SIZE),cursor:Cursor.optional()})),ListCalendarEventsInput=withDateRange(AccountIdInput.extend(ListCalendarEventsQuery.shape),{from:"start",to:"end",maxSpanMs:MAX_EVENT_RANGE_DAYS*DAY_MS}),BusyStatus=named("BusyStatus",z9.enum(["busy","tentative","oof","working_elsewhere","unknown"])),BusyBlock=named("BusyBlock",z9.object({start:Timestamp,end:Timestamp,status:BusyStatus})),AttendeeAvailability=named("AttendeeAvailability",z9.object({address:z9.string(),busy:z9.array(BusyBlock),error:z9.string().nullable()})),Weekday=named("Weekday",z9.enum(["mon","tue","wed","thu","fri","sat","sun"])),TimeOfDay=z9.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/,"Expected HH:MM"),WorkingHours=named("WorkingHours",z9.object({start:TimeOfDay,end:TimeOfDay,days:z9.array(Weekday).min(1).default(["mon","tue","wed","thu","fri"])}).refine((hours)=>hours.end>hours.start,{message:"workingHours end must be after start",path:["end"],params:{reason:"invalid_working_hours"}})),SlotMinutes=z9.number().int().min(MIN_SLOT_MINUTES).max(MAX_SLOT_MINUTES).default(30),AvailabilityOptions={start:QueryTimestamp,end:QueryTimestamp,durationMinutes:SlotMinutes,stepMinutes:SlotMinutes,timeZone:z9.string().min(1).max(MAX_TIME_ZONE_LENGTH).default("UTC"),workingHours:WorkingHours.optional(),maxSuggestions:z9.number().int().min(0).max(MAX_SUGGESTIONS).default(10),requireAll:z9.boolean().default(!1)},FindAvailabilityRequest=named("FindAvailabilityRequest",z9.object({attendees:z9.array(Email).min(1).max(MAX_AVAILABILITY_ATTENDEES),...AvailabilityOptions,includeAccount:z9.boolean().default(!0)})),FindAvailabilityInput=withDateRange(AccountIdInput.extend(FindAvailabilityRequest.shape),{from:"start",to:"end",maxSpanMs:MAX_AVAILABILITY_DAYS*DAY_MS}),TimeSlot=named("TimeSlot",z9.object({start:Timestamp,end:Timestamp})),WithheldReason=named("WithheldReason",z9.enum(["unresolved_attendees"])),Completeness={complete:z9.boolean(),unresolved:z9.array(z9.string()),withheldReason:WithheldReason.nullable()},Availability=named("Availability",z9.object({attendees:z9.array(AttendeeAvailability),suggestions:z9.array(TimeSlot),...Completeness})),AvailabilityMode=named("AvailabilityMode",z9.enum(["all","any"])),FindAccountsAvailabilityRequest=named("FindAccountsAvailabilityRequest",z9.object({accountIds:z9.array(Id).min(1).max(MAX_AVAILABILITY_ACCOUNTS).refine((ids)=>new Set(ids).size===ids.length,"accountIds lists an account twice"),mode:AvailabilityMode.default("all"),attendees:z9.array(Email).max(MAX_AVAILABILITY_ATTENDEES).default([]),...AvailabilityOptions})),FindAccountsAvailabilityInput=withDateRange(FindAccountsAvailabilityRequest,{from:"start",to:"end",maxSpanMs:MAX_AVAILABILITY_DAYS*DAY_MS}),AccountAvailability=named("AccountAvailability",AttendeeAvailability.extend({accountId:Id})),AccountsTimeSlot=named("AccountsTimeSlot",TimeSlot.extend({accountIds:z9.array(Id)})),AccountsAvailability=named("AccountsAvailability",z9.object({accounts:z9.array(AccountAvailability),attendees:z9.array(AttendeeAvailability),suggestions:z9.array(AccountsTimeSlot),...Completeness}));import*as z11 from"zod";import*as z10 from"zod";var customDomainHostname=(example)=>z10.string().trim().toLowerCase().regex(/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){2,}[a-z](?:[a-z0-9-]{0,61}[a-z0-9])$/,`Expected a subdomain such as "${example}"`),CUSTOM_DOMAIN_STATUSES=["pending","active","failed"],customDomainRecord=()=>z10.object({type:z10.string().meta({example:"CNAME"}),name:z10.string(),value:z10.string()});var ConnectDomainHostname=named("ConnectDomainHostname",customDomainHostname("connect.example.com")),ConnectDomainStatus=named("ConnectDomainStatus",z11.enum(CUSTOM_DOMAIN_STATUSES)),ConnectDomainRecord=named("ConnectDomainRecord",customDomainRecord()),ConnectDomain=named("ConnectDomain",z11.object({hostname:ConnectDomainHostname,status:ConnectDomainStatus,records:z11.array(ConnectDomainRecord),ownership:ConnectDomainRecord,error:z11.string().nullable(),activatedAt:Timestamp.nullable(),createdAt:Timestamp})),SetConnectDomain=named("SetConnectDomain",z11.object({hostname:ConnectDomainHostname}));import*as z12 from"zod";var ContactEmailType=named("ContactEmailType",z12.enum(["home","work","other"])),ContactPhoneType=named("ContactPhoneType",z12.enum(["mobile","home","work","other"])),ContactEmail=named("ContactEmail",Address.pick({address:!0}).extend({type:ContactEmailType.nullable()})),ContactPhone=named("ContactPhone",z12.object({number:z12.string(),type:ContactPhoneType.nullable()})),ContactFolder=named("ContactFolder",z12.object({id:Id,name:z12.string(),isDefault:z12.boolean()})),ContactFolderList=List(ContactFolder),Birthday=z12.string().regex(/^(\d{4}-|--)\d{2}-\d{2}$/,"Expected YYYY-MM-DD or --MM-DD"),Contact=named("Contact",z12.object({id:Id,folderId:Id.nullable(),displayName:z12.string().nullable(),givenName:z12.string().nullable(),middleName:z12.string().nullable(),familyName:z12.string().nullable(),nickname:z12.string().nullable(),emails:z12.array(ContactEmail),phones:z12.array(ContactPhone),organization:z12.string().nullable(),jobTitle:z12.string().nullable(),department:z12.string().nullable(),birthday:z12.string().nullable(),notes:z12.string().nullable(),hasPhoto:z12.boolean().nullable(),updatedAt:Timestamp})),ContactPage=Page(Contact),ContactChangedField=named("ContactChangedField",z12.enum([...Contact.keyof().exclude(["id","updatedAt"]).options,"photo"])),ContactEmailInput=named("ContactEmailInput",AddressInput.pick({address:!0}).extend({type:ContactEmailType.optional()})),ContactPhoneInput=named("ContactPhoneInput",z12.object({number:trimmed(MAX_CONTACT_PHONE_LENGTH),type:ContactPhoneType.optional()})),ContactFields={displayName:trimmed(MAX_CONTACT_FIELD_LENGTH),givenName:trimmed(MAX_CONTACT_FIELD_LENGTH),middleName:trimmed(MAX_CONTACT_FIELD_LENGTH),familyName:trimmed(MAX_CONTACT_FIELD_LENGTH),nickname:trimmed(MAX_CONTACT_FIELD_LENGTH),organization:trimmed(MAX_CONTACT_FIELD_LENGTH),jobTitle:trimmed(MAX_CONTACT_FIELD_LENGTH),department:trimmed(MAX_CONTACT_FIELD_LENGTH),birthday:Birthday,notes:z12.string().max(MAX_CONTACT_NOTES_LENGTH)},CreateContact=named("CreateContact",z12.object({folderId:Id.optional(),displayName:ContactFields.displayName.optional(),givenName:ContactFields.givenName.optional(),middleName:ContactFields.middleName.optional(),familyName:ContactFields.familyName.optional(),nickname:ContactFields.nickname.optional(),emails:z12.array(ContactEmailInput).max(MAX_CONTACT_ENTRIES).optional(),phones:z12.array(ContactPhoneInput).max(MAX_CONTACT_ENTRIES).optional(),organization:ContactFields.organization.optional(),jobTitle:ContactFields.jobTitle.optional(),department:ContactFields.department.optional(),birthday:ContactFields.birthday.optional(),notes:ContactFields.notes.optional()})),CreateContactInput=AccountIdInput.extend(CreateContact.shape).refine((c)=>c.displayName!==void 0||c.givenName!==void 0||c.familyName!==void 0||c.organization!==void 0||(c.emails?.length??0)>0||(c.phones?.length??0)>0,{message:"A contact needs a name, an organization, an email address or a phone number",params:{reason:"contact_unidentifiable"}}),UpdateContact=named("UpdateContact",z12.object({displayName:ContactFields.displayName.nullable().optional(),givenName:ContactFields.givenName.nullable().optional(),middleName:ContactFields.middleName.nullable().optional(),familyName:ContactFields.familyName.nullable().optional(),nickname:ContactFields.nickname.nullable().optional(),emails:z12.array(ContactEmailInput).max(MAX_CONTACT_ENTRIES).optional(),phones:z12.array(ContactPhoneInput).max(MAX_CONTACT_ENTRIES).optional(),organization:ContactFields.organization.nullable().optional(),jobTitle:ContactFields.jobTitle.nullable().optional(),department:ContactFields.department.nullable().optional(),birthday:ContactFields.birthday.nullable().optional(),notes:ContactFields.notes.nullable().optional()})),ContactIdInput=named("ContactIdInput",AccountIdInput.extend({contactId:Id})),ListContactsQuery=named("ListContactsQuery",z12.object({q:trimmed(MAX_FILTER_QUERY_LENGTH).optional(),folderId:Id.optional(),limit:pageLimit(MAX_CONTACT_PAGE_SIZE,DEFAULT_CONTACT_PAGE_SIZE),cursor:Cursor.optional()}));import*as z13 from"zod";var Reachability=named("Reachability",z13.enum(["safe","risky","invalid","unknown"])),CreateEmailValidation=named("CreateEmailValidation",z13.object({addresses:z13.array(Email).min(1).max(MAX_EMAIL_VALIDATIONS),suppressInvalid:z13.boolean().default(!1),refresh:z13.boolean().default(!1)})),EmailValidationResult=named("EmailValidationResult",z13.object({address:z13.string(),reachability:Reachability,validSyntax:z13.boolean(),domainAcceptsMail:z13.boolean().nullable(),deliverable:z13.boolean().nullable(),disabled:z13.boolean().nullable(),fullInbox:z13.boolean().nullable(),catchAll:z13.boolean().nullable(),disposable:z13.boolean().nullable(),roleAccount:z13.boolean().nullable(),suppressed:z13.boolean(),cached:z13.boolean(),checkedAt:Timestamp})),EmailValidation=named("EmailValidation",z13.object({results:z13.array(EmailValidationResult)})),EmailValidationRecord=named("EmailValidationRecord",z13.object({id:Id,createdAt:Timestamp,...EmailValidationResult.shape})),EmailValidationRecordPage=Page(EmailValidationRecord),ListEmailValidationsQuery=named("ListEmailValidationsQuery",z13.object({address:z13.string().trim().max(MAX_EMAIL_LENGTH).optional(),reachability:Reachability.optional(),...Paged}));import{oc}from"@orpc/contract";import*as z17 from"zod";import*as z15 from"zod";import*as z14 from"zod";var PlanCreditPack=named("PlanCreditPack",z14.object({credits:z14.number().int().min(1).max(MAX_CREDIT_PACK_CREDITS),amount:z14.number().int().min(0)})),PlanCreditPacks=z14.array(PlanCreditPack).max(MAX_CREDIT_PACKS_PER_PLAN).superRefine((packs,ctx)=>{for(let{index,field,message}of creditPackProblems(packs))ctx.addIssue({code:"custom",path:[index,field],message})});function creditPackProblems(packs){let problems=[];return packs.forEach((pack,index)=>{let before=packs[index-1];if(!before)return;if(pack.credits<=before.credits)problems.push({index,field:"credits",message:"Credits must be more than the pack before"});else if(BigInt(pack.amount)*BigInt(before.credits)>BigInt(before.amount)*BigInt(pack.credits))problems.push({index,field:"amount",message:"The price per credit must not be more than the pack before's"})}),problems}var PlanModules=named("PlanModules",z15.object({mail:z15.boolean(),calendar:z15.boolean(),contacts:z15.boolean(),tracking:z15.boolean(),ai:z15.boolean(),meetings:z15.boolean(),sharedMailboxes:z15.boolean()}));var BILLING_COMPONENTS=["mail","calendarContacts","ai","sharedMailboxes","meetings"],COMPONENT_MODULES={mail:["mail"],calendarContacts:["calendar","contacts"],ai:["ai"],sharedMailboxes:["sharedMailboxes"],meetings:["meetings"]},MODULE_TOGGLES=[...BILLING_COMPONENTS,"tracking"];var TOGGLE_MODULES={...COMPONENT_MODULES,tracking:["tracking"]};var METERS=["mailAccounts","calendarContactsAccounts","meetingAccounts","sends","aiClassifications","aiDrafts","storage"],Meter=named("Meter",z15.enum(METERS)),PlanIncluded=named("PlanIncluded",z15.object({mailAccounts:z15.number().int().min(0).nullable(),calendarContactsAccounts:z15.number().int().min(0).nullable(),meetingAccounts:z15.number().int().min(0).nullable(),sendsPerMonth:z15.number().int().min(0).nullable(),aiClassificationsPerMonth:z15.number().int().min(0).nullable(),aiDraftsPerMonth:z15.number().int().min(0).nullable(),storageBytes:z15.number().int().min(0).nullable(),validationCredits:z15.number().int().min(0).max(MAX_VALIDATION_CREDITS).nullable()})),Amount=z15.number().int().min(0),PlanPricing=named("PlanPricing",z15.object({currency:z15.string().regex(/^[a-z]{3}$/),baseMonthly:Amount,baseAnnual:Amount.nullable(),components:z15.object({mail:Amount,calendarContacts:Amount,ai:Amount,sharedMailboxes:Amount,meetings:Amount}),sendsPer1k:Amount,aiClassificationsPer1k:Amount,aiDraftsPer1k:Amount,storagePerGbMonth:Amount,creditPacks:PlanCreditPacks})),MIN_AUDIT_LOG_RETENTION_DAYS=1,MAX_AUDIT_LOG_RETENTION_DAYS=3650,PlanFeatures=named("PlanFeatures",z15.object({sso:z15.boolean(),scim:z15.boolean(),auditLogRetentionDays:z15.number().int().min(MIN_AUDIT_LOG_RETENTION_DAYS).max(MAX_AUDIT_LOG_RETENTION_DAYS).nullable(),sla:z15.boolean(),customDpa:z15.boolean(),prioritySupport:z15.boolean()})),GATED_PLAN_FEATURES=["sso","scim"],PlanLimits=named("PlanLimits",z15.object({maxAccounts:z15.number().int().min(0).nullable(),sendsPerDay:z15.number().int().min(0).nullable(),emailValidationsPerDay:z15.number().int().min(0).nullable(),apiCallsPerMinute:z15.number().int().min(1).nullable(),apiCallsPerMinutePerKey:z15.number().int().min(1).nullable(),webhookEndpoints:z15.number().int().min(0).nullable(),bodyStorageAllowed:z15.boolean(),storageBytes:z15.number().int().min(0).nullable(),included:PlanIncluded,pricing:PlanPricing.nullable(),features:PlanFeatures}));import*as z16 from"zod";var TemplateSource=named("TemplateSource",z16.enum(["handlebars","react-email"])),TemplateFormat=named("TemplateFormat",z16.enum(["html","markdown"])),TemplateField=named("TemplateField",z16.enum(["subject","html","text","previewText"])),TemplateFields=named("TemplateFields",z16.object({subject:z16.string().max(MAX_TEMPLATE_SUBJECT_LENGTH).nullable(),html:z16.string().max(MAX_TEMPLATE_BODY_LENGTH).nullable(),text:z16.string().max(MAX_TEMPLATE_BODY_LENGTH).nullable(),previewText:z16.string().max(MAX_TEMPLATE_PREVIEW_TEXT_LENGTH).nullable(),format:TemplateFormat})),TemplateSummary=named("TemplateSummary",z16.object({id:Id,accountId:Id.nullable(),name:z16.string(),description:z16.string().nullable(),source:TemplateSource,externalRef:z16.string().nullable(),createdAt:Timestamp,updatedAt:Timestamp})),Template=named("Template",TemplateSummary.extend(TemplateFields.shape)),TemplateSummaryPage=Page(TemplateSummary),RenderParams=named("RenderParams",z16.record(z16.string(),z16.unknown())),RenderedTemplate=named("RenderedTemplate",z16.object({subject:z16.string().nullable(),html:z16.string().nullable(),text:z16.string().nullable(),previewText:z16.string().nullable()})),TemplateIdInput=named("TemplateIdInput",z16.object({templateId:Id})),TemplateName=trimmed(MAX_TEMPLATE_NAME_LENGTH),TemplateDescription=z16.string().max(MAX_TEMPLATE_DESCRIPTION_LENGTH),ListTemplatesQuery=named("ListTemplatesQuery",z16.object({...Paged,accountId:Id.optional(),usableBy:Id.optional(),name:z16.string().max(MAX_TEMPLATE_NAME_LENGTH).optional(),query:z16.string().trim().max(MAX_TEMPLATE_NAME_LENGTH).optional()})),CreateTemplate=named("CreateTemplate",z16.object({accountId:Id.optional(),name:TemplateName,description:TemplateDescription.optional(),subject:TemplateFields.shape.subject.unwrap().optional(),html:TemplateFields.shape.html.unwrap().optional(),text:TemplateFields.shape.text.unwrap().optional(),previewText:TemplateFields.shape.previewText.unwrap().optional(),format:TemplateFormat.default("html"),source:TemplateSource.default("handlebars"),externalRef:ExternalRef.optional()})),UpdateTemplate=named("UpdateTemplate",z16.object({name:TemplateName.optional(),description:TemplateDescription.nullable().optional(),subject:TemplateFields.shape.subject.optional(),html:TemplateFields.shape.html.optional(),text:TemplateFields.shape.text.optional(),previewText:TemplateFields.shape.previewText.optional(),format:TemplateFormat.optional(),source:TemplateSource.optional(),externalRef:ExternalRef.nullable().optional()})),RenderTemplateRequest=named("RenderTemplateRequest",z16.object({templateId:Id.optional(),accountId:Id.optional(),subject:z16.string().max(MAX_TEMPLATE_SUBJECT_LENGTH).optional(),html:z16.string().max(MAX_TEMPLATE_BODY_LENGTH).optional(),text:z16.string().max(MAX_TEMPLATE_BODY_LENGTH).optional(),previewText:z16.string().max(MAX_TEMPLATE_PREVIEW_TEXT_LENGTH).optional(),format:TemplateFormat.optional(),params:RenderParams.default({})}).superRefine((request,ctx)=>{let{templateId,subject,html,text,previewText,format}=request;if(templateId===void 0)return;if([subject,html,text,previewText,format].some((field)=>field!==void 0))ctx.addIssue({code:"custom",path:["templateId"],message:"Render either a template or inline fields, not both",params:{reason:"template_and_inline_fields"}})}));var InputIssue=named("InputIssue",z17.object({path:z17.array(z17.union([z17.string(),z17.number()])),message:z17.string()})),InvalidInputData=named("InvalidInputData",z17.object({issues:z17.array(InputIssue)})),UnknownIdsData=named("UnknownIdsData",z17.object({ids:z17.array(Id)})),SuppressedData=named("SuppressedData",z17.object({addresses:z17.array(z17.string())})),SyncKindsData=named("SyncKindsData",z17.object({kinds:z17.array(SyncKind)})),PlanFeatureData=named("PlanFeatureData",z17.object({feature:z17.enum(GATED_PLAN_FEATURES)})),TemplateFieldData=named("TemplateFieldData",z17.object({field:TemplateField})),SsoRequiredData=named("SsoRequiredData",z17.object({providerId:z17.string(),organization:z17.string()})),PLAN_LIMIT_KINDS=["mail_accounts","calendar_contacts_accounts","meeting_accounts","accounts","sends_per_day","sends_per_month","webhook_endpoints","validations_per_day","storage_bytes","body_storage","ai_classifications","ai_drafts","validation_credits","spend_cap","plan"],PlanLimitKind=named("PlanLimitKind",z17.enum(PLAN_LIMIT_KINDS)),PlanLimitData=named("PlanLimitData",z17.object({limit:PlanLimitKind,value:z17.number().nullable(),usage:z17.number().nullable(),upgradeUrl:z17.string()})),ValidationCreditsData=named("ValidationCreditsData",PlanLimitData.extend({balance:z17.number().int(),needed:z17.number().int()})),LIMIT_DATA_REASONS=["plan_limit_accounts","plan_limit_sends","plan_limit_validations","plan_limit_ai","plan_limit_webhooks","plan_limit_storage","plan_required","validation_credits_exhausted","spend_cap_reached"],LIMIT_HIT_REASONS=[...LIMIT_DATA_REASONS,"plan_feature_unavailable"],LimitHitReason=named("LimitHitReason",z17.enum(LIMIT_HIT_REASONS)),REASONS={invalid_input:{status:400,data:InvalidInputData},invalid_cursor:{status:400},invalid_range:{status:400},payload_too_large:{status:413},invalid_payload:{status:400},invalid_signature:{status:400},route_not_found:{status:404},internal_error:{status:500},invalid_idempotency_key:{status:400},idempotency_key_reused:{status:422},idempotency_key_in_progress:{status:409},rate_limited:{status:429},too_many_requests:{status:429},overloaded:{status:503},unauthenticated:{status:401},api_key_expired:{status:401},missing_scope:{status:403},role_forbidden:{status:403},account_token_forbidden:{status:403},account_scope_forbidden:{status:403},api_key_required:{status:403},mcp_only_key:{status:403},session_required:{status:403},owner_required:{status:403},platform_admin_required:{status:403},email_verification_required:{status:403},console_only:{status:403},sso_required:{status:403,data:SsoRequiredData},two_factor_required:{status:403},session_not_fresh:{status:403},cross_site_request:{status:403},plan_limit_accounts:{status:403,data:PlanLimitData},plan_limit_sends:{status:403,data:PlanLimitData},plan_limit_validations:{status:403,data:PlanLimitData},plan_limit_ai:{status:403,data:PlanLimitData},plan_limit_webhooks:{status:403,data:PlanLimitData},plan_limit_storage:{status:403,data:PlanLimitData},plan_required:{status:402,data:PlanLimitData},spend_cap_reached:{status:402,data:PlanLimitData},plan_module_unavailable:{status:403},plan_feature_unavailable:{status:403,data:PlanFeatureData},storage_unavailable:{status:400},connect_grants_nothing:{status:400},mail_permissions_unsupported:{status:400},tenant_not_found:{status:404},billing_past_due:{status:402},billing_disabled:{status:404},billing_unavailable:{status:502},billing_tax_failed:{status:502},billing_subscription_exists:{status:409},billing_customer_missing:{status:409},plan_not_billable:{status:409},plan_not_offered:{status:409},billing_subscription_missing:{status:409},billing_not_active:{status:409},billing_cancel_pending:{status:409},plan_unchanged:{status:409},plan_currency_mismatch:{status:409},plan_interval_unavailable:{status:409},plan_interval_change:{status:409},billing_payment_failed:{status:402},plan_change_quote_changed:{status:409},billing_checkout_in_progress:{status:409},validation_credits_exhausted:{status:402,data:ValidationCreditsData},account_not_found:{status:404},unknown_account:{status:422,data:UnknownIdsData},account_paused:{status:409},account_disabled:{status:409},account_reauth_required:{status:409},account_misconfigured:{status:409},mail_disabled:{status:409},mail_permission_missing:{status:409},calendar_disabled:{status:409},contacts_disabled:{status:409},sync_kinds_disabled:{status:409,data:SyncKindsData},mcp_key_not_found:{status:404},mcp_key_limit:{status:409},provider_not_found:{status:404},provider_throttled:{status:429},provider_unavailable:{status:502},provider_forbidden:{status:403},provider_conflict:{status:409},provider_unsupported:{status:400},provider_rejected:{status:400},outcome_unknown:{status:502},message_not_found:{status:404},unknown_mailbox:{status:422,data:UnknownIdsData},mailbox_not_label:{status:400},mailbox_protected:{status:409},mailbox_parent:{status:400},thread_not_found:{status:404},identity_not_found:{status:404},identities_managed_by_provider:{status:400},identity_exists:{status:409},identity_not_custom:{status:400},draft_not_found:{status:404},draft_lost:{status:502},send_not_found:{status:404},mail_merge_not_found:{status:404},attachments_too_large:{status:400},not_a_draft:{status:400},unknown_reply_target:{status:422},from_not_allowed:{status:400},tracking_unsupported:{status:400},honeypot_needs_click_tracking:{status:400},list_send_recipients:{status:400},suppressed:{status:400,data:SuppressedData},no_recipients:{status:400},list_unsubscribe_unavailable:{status:400},send_not_reschedulable:{status:409},send_not_cancellable:{status:409},duplicate_message_id:{status:409},suppression_list_not_found:{status:404},suppression_entry_not_found:{status:404},suppression_review_not_found:{status:404},suppression_list_exists:{status:409},suppression_list_not_empty:{status:409},tracking_domain_unavailable:{status:400},tracking_domain_not_found:{status:404},tracking_domain_not_allowed:{status:400},tracking_domain_taken:{status:409},tracking_domain_edge_unavailable:{status:502},connect_domain_unavailable:{status:400},connect_domain_not_found:{status:404},connect_domain_not_allowed:{status:400},connect_domain_taken:{status:409},connect_domain_in_use:{status:409},connect_domain_edge_unavailable:{status:502},connect_domain_inactive:{status:409},connect_domain_unsupported:{status:400},template_not_found:{status:404},unknown_template:{status:422},template_and_inline_fields:{status:400},invalid_template:{status:400,data:TemplateFieldData},template_account_mismatch:{status:400},template_render_failed:{status:400,data:TemplateFieldData},params_too_large:{status:400},template_name_taken:{status:409},calendar_not_found:{status:404},calendar_event_not_found:{status:404},contact_not_found:{status:404},photo_not_found:{status:404},unknown_calendar:{status:422},unknown_contact_folder:{status:422},calendar_read_only:{status:403},calendar_primary:{status:409},organizer_cannot_respond:{status:409},invalid_time_zone:{status:400},invalid_working_hours:{status:400},scheduling_link_not_found:{status:404},booking_not_found:{status:404},slot_unavailable:{status:409},slot_taken:{status:409},booking_cancelled:{status:409},mixed_event_times:{status:400},invalid_recurrence:{status:400},recurrence_on_instance:{status:400},contact_unidentifiable:{status:400},multiple_meeting_options:{status:400},meeting_service_not_connected:{status:409},meeting_service_reauth_required:{status:409},meeting_host_missing:{status:409},meeting_recurrence_unsupported:{status:400},meeting_service_rejected:{status:400},meeting_deleted:{status:409},meeting_service_unavailable:{status:502},meeting_app_not_found:{status:404},unknown_meeting_app:{status:422},meeting_connection_not_found:{status:404},meeting_connect_session_not_found:{status:404},meeting_app_exists:{status:409},meeting_app_in_use:{status:409},meeting_app_mismatch:{status:400},meeting_app_secret_unreadable:{status:400},not_testable:{status:400},autodiscovery_failed:{status:422},password_required:{status:400},oauth_servers_fixed:{status:400},dav_discovery_failed:{status:400},dav_unsupported:{status:400},login_failed:{status:400},server_not_allowed:{status:400},plaintext_not_allowed:{status:400},not_dav_server:{status:400},not_imap_account:{status:400},password_not_stored:{status:400},password_unsupported:{status:400},connect_session_not_found:{status:404},session_used:{status:409},session_expired:{status:400},provider_not_allowed:{status:400},redirect_uri_not_allowed:{status:400},access_denied:{status:400},access_token_rejected:{status:400},account_exists:{status:409},account_mismatch:{status:409},consent_failed:{status:400},consent_revoked:{status:400},consent_unverified:{status:400},credential_conflict:{status:400},credential_missing:{status:400},identity_failed:{status:400},identity_unavailable:{status:400},invalid_client:{status:400},interaction_required:{status:400},invalid_grant:{status:400},missing_refresh_token:{status:400},unknown_oauth_app:{status:422},oauth_app_not_found:{status:404},oauth_app_mismatch:{status:400},oauth_app_in_use:{status:409},oauth_app_secret_unreadable:{status:400},google_imap_requires_own_app:{status:400},org_connection_account:{status:400},shared_mailbox_account:{status:400},gmail_push_unsupported:{status:400},push_unavailable:{status:400},cloud_required:{status:400},provider_error:{status:400},provider_not_configured:{status:400},unsupported_provider:{status:400},scope_denied:{status:400},token_exchange_failed:{status:400},org_connection_not_found:{status:404},org_connection_secret_unreadable:{status:400},consent_required:{status:409},delegate_missing:{status:400},directory_unavailable:{status:409},directory_already_connected:{status:409},directory_mismatch:{status:400},directory_id_not_allowed:{status:400},credential_mismatch:{status:400},connection_in_use:{status:409},invalid_certificate:{status:400},invalid_service_account_key:{status:400},not_microsoft:{status:400},user_required:{status:400},shared_mailbox_unsupported:{status:400},scope_missing:{status:400},shared_mailbox_inaccessible:{status:422},group_unsupported:{status:400},group_relay_inaccessible:{status:422},group_alias_unverified:{status:409},group_alias_missing:{status:409},webhook_endpoint_not_found:{status:404},endpoint_disabled:{status:409},event_not_found:{status:404},replay_range_too_large:{status:400},mailbox_outside_accounts:{status:400},https_required:{status:400},export_not_found:{status:404},export_not_ready:{status:409},export_failed:{status:409},export_running:{status:409},export_in_progress:{status:409},export_expired:{status:410},export_corrupt:{status:500},confirmation_mismatch:{status:400},erase_scheduled:{status:409},erase_started:{status:409},erase_not_found:{status:404},ai_disabled:{status:400},ai_unavailable:{status:502},ai_suspended:{status:502},ai_refused:{status:400},unknown_message:{status:422},email_validation_disabled:{status:400},email_validation_unavailable:{status:502},email_validation_suspended:{status:502}};function reasonGuards(table){let isReason=(code)=>typeof code==="string"&&Object.hasOwn(table,code);return{isReason,reasonOf(error){if(typeof error!=="object"||error===null||!("code"in error))return null;return isReason(error.code)?error.code:null}}}var v1Guards=reasonGuards(REASONS),{isReason:isErrorReason,reasonOf}=v1Guards;function errorMap(table,conditions){let map={};for(let[reason,message]of Object.entries(conditions)){let definition=table[reason];map[reason]={status:definition.status,message,...definition.data!==void 0&&{data:definition.data}}}return map}function errors(conditions){return errorMap(REASONS,conditions)}var GLOBAL_ERRORS=errors({invalid_input:"The request does not match the operation's input schema (a malformed body included); data.issues names each problem.",unauthenticated:"No usable credential: none was sent, or it is malformed, unknown, revoked or an expired account token.",api_key_expired:"The API key is past its expiry (or the grace of the rotation that replaced it).",missing_scope:"The API key lacks the scope the operation needs.",role_forbidden:"The member's role does not allow the operation.",account_token_forbidden:"An account token called an operation account tokens cannot call.",account_scope_forbidden:"A credential limited to specific accounts called an operation that reaches beyond them.",mcp_only_key:"An MCP-only key (minted for an end user's MCP client) was used outside the MCP server.",rate_limited:"The tenant used up its plan's request rate; Retry-After says when the window ends.",too_many_requests:"The credential has too many requests in flight at once; retry after Retry-After.",overloaded:"The API is shedding load; retry after Retry-After.",payload_too_large:"The request body is larger than the API accepts.",internal_error:"An unexpected failure on our side.",invalid_idempotency_key:"POST only: the Idempotency-Key header is not 1 to 255 printable ASCII characters.",idempotency_key_reused:"POST only: the Idempotency-Key was used in the last 24 hours with another method, path or body.",idempotency_key_in_progress:"POST only: the request first sent with this Idempotency-Key has not been answered yet."}),base=oc.errors(GLOBAL_ERRORS),BOARD_SESSION_ONLY="The caller is not a console session (only console sessions may call this).",SESSION_ONLY=errors({session_required:BOARD_SESSION_ONLY}),OWNER_ONLY=errors({session_required:BOARD_SESSION_ONLY,owner_required:"The caller does not own the organization."}),PLATFORM_ONLY=errors({session_required:BOARD_SESSION_ONLY,platform_admin_required:"The caller is not a platform administrator.",email_verification_required:"The platform administrator has not verified their email address yet."}),ACCOUNT_NOT_FOUND=errors({account_not_found:"The account does not exist, or the caller is an account token for another account."}),MAIL_ACCOUNT={...planModule("mail"),...ACCOUNT_NOT_FOUND,...errors({mail_disabled:"The account's mail is off (settings.mailEnabled)."})},MAIL_CHANGE=errors({mail_permission_missing:"The account's grant cannot change the mailbox (mailPermissions read_send or read); reconnect it with options.mailPermissions full."}),MAIL_SEND=errors({mail_permission_missing:"The account's grant cannot send (mailPermissions read); reconnect it with options.mailPermissions read_send or full."});function accountState(account="The account"){return errors({account_paused:`${account} is paused.`,account_disabled:`${account} is disabled.`,account_reauth_required:`${account} must be reconnected: its stored grant or login no longer works.`,account_misconfigured:`${account}'s stored grant cannot be used until an administrator fixes what it relies on (the tenant's OAuth app or organization connection is refused, missing or unreadable).`})}var PROVIDER_FAILURES=errors({provider_throttled:"The provider asked us to slow down (with Retry-After when it named a wait).",provider_unavailable:"The provider could not be reached (with Retry-After when it named a wait)."});function bodyStorage(field){return errors({storage_unavailable:`${field} is cache or full and no object store is configured.`,plan_limit_storage:`${field} is cache or full and the tenant's plan does not include body storage.`})}function planModule(module){return errors({plan_module_unavailable:`The tenant's plan does not include ${module}.`})}var PLAN_REQUIRED_CONDITION="The workspace has no plan and is not its owner's Free workspace (the Free plan covers one workspace per owner): choose a plan under Billing.",PLAN_REQUIRED=errors({plan_required:PLAN_REQUIRED_CONDITION});function spendCapCondition(what){return`The workspace's spend cap is reached and its action is pause: refused when ${what}, which would be billed. Raising or clearing the cap lifts it.`}var PLAN_LIMIT_ACCOUNTS=errors({plan_limit_accounts:"The tenant already has as many accounts as its plan allows.",plan_required:PLAN_REQUIRED_CONDITION,spend_cap_reached:spendCapCondition("the change would take an account count past what the plan includes")}),BILLING_PAST_DUE=errors({billing_past_due:"The tenant's payment is past due beyond the grace period (sends already queued or scheduled still go out)."}),INVALID_CURSOR=errors({invalid_cursor:"The cursor is not one this list issued."});import*as z21 from"zod";import*as z18 from"zod";var MailboxRole=named("MailboxRole",z18.enum(["inbox","sent","drafts","trash","junk","archive","all","important","custom"])),MailboxKind=named("MailboxKind",z18.enum(["folder","label"])),Mailbox=named("Mailbox",z18.object({id:Id,name:z18.string(),path:z18.string(),parentId:Id.nullable(),role:MailboxRole,kind:MailboxKind,totalCount:z18.number().int().nullable(),unreadCount:z18.number().int().nullable()})),MailboxList=List(Mailbox),MailboxIdInput=named("MailboxIdInput",AccountIdInput.extend({mailboxId:Id})),CreateMailbox=named("CreateMailbox",z18.object({name:trimmed(MAX_DISPLAY_NAME_LENGTH),parentId:Id.optional()})),CreateMailboxInput=named("CreateMailboxInput",AccountIdInput.extend(CreateMailbox.shape)),UpdateMailbox=named("UpdateMailbox",z18.object({name:trimmed(MAX_DISPLAY_NAME_LENGTH).optional(),parentId:Id.nullable().optional()})),MailboxChangedField=named("MailboxChangedField",z18.enum(["name","path","parentId","role","kind"])),MessageDirection=named("MessageDirection",z18.enum(["inbound","outbound"])),MessagePriority=named("MessagePriority",z18.enum(["high","normal","low"])),MessageSummary=named("MessageSummary",z18.object({id:Id,threadId:ProviderId.nullable(),internetMessageId:z18.string().nullable(),mailboxIds:z18.array(Id),subject:z18.string().nullable(),from:Address.nullable(),to:z18.array(Address),cc:z18.array(Address),snippet:z18.string().nullable(),receivedAt:Timestamp.nullable(),sentAt:Timestamp.nullable(),isRead:z18.boolean(),isFlagged:z18.boolean(),isDraft:z18.boolean(),hasAttachments:z18.boolean(),categories:z18.array(z18.string()),direction:MessageDirection,outboxId:Id.nullable(),reference:z18.string().nullable(),importance:MessagePriority.nullable()})),MessageChangedField=named("MessageChangedField",z18.enum(["threadId","internetMessageId","subject","from","to","cc","bcc","replyTo","snippet","receivedAt","sentAt","isRead","isFlagged","isDraft","hasAttachments","categories","mailboxIds","direction","outboxId","importance"])),MessageSummaryPage=Page(MessageSummary),Attachment=named("Attachment",z18.object({id:ProviderId,filename:z18.string().nullable(),contentType:z18.string(),size:z18.number().int().nullable(),isInline:z18.boolean(),contentId:z18.string().nullable()})),ReplyKind=named("ReplyKind",z18.enum(["opt_out","auto_reply","bounce","human_reply"])),AutoReplyCategory=named("AutoReplyCategory",z18.enum(["out_of_office","left_company","address_changed","ticket_acknowledgment","challenge_response","other"])),MessageClassification=named("MessageClassification",z18.object({kind:ReplyKind,probabilities:z18.record(z18.string(),z18.number()),optOutProbability:z18.number(),autoReplyCategory:AutoReplyCategory.nullable(),returnDate:DateOnly.nullable(),alternateContacts:z18.array(z18.string()),outboxId:Id.nullable(),model:z18.string(),classifiedAt:Timestamp})),MessageBody=named("MessageBody",z18.object({text:z18.string().nullable(),html:z18.string().nullable()})),MessageHeader=named("MessageHeader",z18.object({name:z18.string(),value:z18.string()})),Message=named("Message",MessageSummary.extend({bcc:z18.array(Address),replyTo:z18.array(Address),body:MessageBody,attachments:z18.array(Attachment),classification:MessageClassification.nullable(),inReplyTo:z18.string().nullable(),references:z18.array(z18.string()),headers:z18.array(MessageHeader).optional()})),MessagePage=Page(Message),SearchSource=named("SearchSource",z18.enum(["auto","index","provider"])),SearchResultSource=named("SearchResultSource",SearchSource.exclude(["auto"])),SearchHit=named("SearchHit",MessageSummary.extend({id:Id.nullable()})),SearchPage=named("SearchPage",z18.object({...pageFields(SearchHit),source:SearchResultSource})),BodyStorageUsage=named("BodyStorageUsage",z18.object({available:z18.boolean(),storedMessages:z18.number().int(),storedBytes:z18.number().int(),cachedBytes:z18.number().int(),cacheBudgetBytes:z18.number().int(),cacheMaxAgeDays:z18.number().int()})),MessageIdInput=named("MessageIdInput",AccountIdInput.extend({messageId:Id})),AttachmentIdInput=named("AttachmentIdInput",MessageIdInput.extend({attachmentId:ProviderId})),MessageDetailInclude=named("MessageDetailInclude",z18.enum(["headers"])),MessageListInclude=named("MessageListInclude",z18.enum(["body","headers"])),includeList=(item)=>z18.union([z18.string(),z18.array(item)]).transform((v)=>Array.isArray(v)?v:v.split(",").map((s)=>s.trim())).pipe(z18.array(item)),GetMessageInput=named("GetMessageInput",MessageIdInput.extend({include:includeList(MessageDetailInclude).optional()})),MessageFilters={from:trimmed(MAX_EMAIL_LENGTH).optional(),to:trimmed(MAX_EMAIL_LENGTH).optional(),unread:QueryBoolean.optional(),flagged:QueryBoolean.optional(),hasAttachments:QueryBoolean.optional()},ListMessagesQuery=named("ListMessagesQuery",z18.object({mailboxId:Id.optional(),...MessageFilters,importance:MessagePriority.optional(),outboxId:Id.optional(),internetMessageId:z18.string().max(MAX_MESSAGE_ID_LENGTH).regex(/^<[^<>\s]+>$/,"internetMessageId must be one <id>").optional(),after:QueryTimestamp.optional(),before:QueryTimestamp.optional(),include:includeList(MessageListInclude).optional(),limit:z18.coerce.number().int().min(1).max(MAX_PAGE_SIZE).optional(),cursor:Cursor.optional()}));function withListIncludeRules(schema){return schema.superRefine((value,ctx)=>{let{include,limit}=value;if(include?.includes("headers")&&!include.includes("body"))ctx.addIssue({code:"custom",path:["include"],message:"headers needs body in include"});if(include?.includes("body")&&limit!==void 0&&limit>MAX_MESSAGE_BODIES_PAGE_SIZE)ctx.addIssue({code:"custom",path:["limit"],message:`limit is at most ${MAX_MESSAGE_BODIES_PAGE_SIZE} with include=body`})})}var SearchMessagesQuery=named("SearchMessagesQuery",z18.object({q:trimmed(MAX_SEARCH_QUERY_LENGTH),mailboxId:Id.optional(),...MessageFilters,after:QueryTimestamp.optional(),before:QueryTimestamp.optional(),source:SearchSource.default("auto"),limit:pageLimit(MAX_SEARCH_PAGE_SIZE,DEFAULT_SEARCH_PAGE_SIZE),cursor:Cursor.optional()})),UpdateMessage=named("UpdateMessage",z18.object({isRead:z18.boolean().optional(),isFlagged:z18.boolean().optional(),mailboxId:Id.optional(),addMailboxIds:z18.array(Id).max(MAX_MAILBOX_CHANGES).optional(),removeMailboxIds:z18.array(Id).max(MAX_MAILBOX_CHANGES).optional()})),DeleteMessageQuery=named("DeleteMessageQuery",z18.object({permanent:QueryBoolean.default(!1)})),DeleteMessageInput=named("DeleteMessageInput",z18.object({params:MessageIdInput,query:DeleteMessageQuery}));import*as z20 from"zod";import*as z19 from"zod";var GLOBAL_LIST_ID="global";var LIST_ID_PATTERN=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/,SuppressionListId=z19.string().trim().toLowerCase().regex(LIST_ID_PATTERN),ListId=SuppressionListId.refine((id)=>id!==GLOBAL_LIST_ID,{message:`"${GLOBAL_LIST_ID}" is reserved`}),Reason=trimmed(MAX_SUPPRESSION_REASON_LENGTH),ListName=Name.trim().min(1),ListDescription=z19.string().trim().max(MAX_SUPPRESSION_LIST_DESCRIPTION_LENGTH),SuppressionSource=named("SuppressionSource",z19.enum(["unsubscribe","bounce","api","reply","complaint","inactive","invalid"])),DetectedOptOutSource=named("DetectedOptOutSource",SuppressionSource.extract(["reply","complaint","inactive"])),SuppressionEntry=named("SuppressionEntry",z19.object({address:z19.string(),source:SuppressionSource,reason:z19.string().nullable(),createdAt:Timestamp})),SuppressionEntryPage=Page(SuppressionEntry),SuppressionCounts=named("SuppressionCounts",z19.object({unsubscribe:z19.number().int(),bounce:z19.number().int(),api:z19.number().int(),reply:z19.number().int(),complaint:z19.number().int(),inactive:z19.number().int(),invalid:z19.number().int()})),SuppressionList=named("SuppressionList",z19.object({id:z19.string(),name:z19.string(),description:z19.string().nullable(),count:z19.number().int(),counts:SuppressionCounts,createdAt:Timestamp.nullable()})),SuppressionListList=List(SuppressionList),SuppressionReview=named("SuppressionReview",z19.object({id:Id,listId:z19.string(),address:z19.string(),source:DetectedOptOutSource,accountId:Id,messageId:Id,probability:z19.number(),alternateContacts:z19.array(z19.string()),createdAt:Timestamp})),SuppressionReviewPage=Page(SuppressionReview),ListIdInput=named("ListIdInput",z19.object({listId:ListId})),SuppressionListIdInput=named("SuppressionListIdInput",z19.object({listId:SuppressionListId})),ReviewIdInput=named("ReviewIdInput",z19.object({reviewId:Id})),CreateSuppressionList=named("CreateSuppressionList",z19.object({id:ListId,name:ListName,description:ListDescription.nullable().optional()})),UpdateSuppressionList=named("UpdateSuppressionList",z19.object({name:ListName.optional(),description:ListDescription.nullable().optional()})),DeleteSuppressionListQuery=named("DeleteSuppressionListQuery",z19.object({withEntries:QueryBoolean.default(!1)})),DeleteSuppressionListInput=named("DeleteSuppressionListInput",z19.object({params:ListIdInput,query:DeleteSuppressionListQuery})),ListSuppressionEntriesQuery=named("ListSuppressionEntriesQuery",z19.object({address:z19.string().trim().max(MAX_EMAIL_LENGTH).optional(),source:SuppressionSource.optional(),...Paged})),AddSuppressionEntriesRequest=named("AddSuppressionEntriesRequest",z19.object({addresses:z19.array(Email).min(1).max(MAX_SUPPRESSION_BATCH),reason:Reason.optional()})),SuppressionEntryInput=named("SuppressionEntryInput",SuppressionListIdInput.extend({address:trimmed(MAX_EMAIL_LENGTH)})),ListSuppressionReviewsQuery=named("ListSuppressionReviewsQuery",z19.object({listId:SuppressionListId.optional(),...Paged})),SuppressionAdded=named("SuppressionAdded",z19.object({added:z19.boolean()})),SuppressionEntriesAdded=named("SuppressionEntriesAdded",z19.object({added:z19.number().int(),alreadyListed:z19.number().int()}));var AttachmentInput=named("AttachmentInput",z20.object({filename:z20.string().min(1).max(MAX_FILENAME_LENGTH),contentType:z20.string().min(1).max(MAX_CONTENT_TYPE_LENGTH),content:z20.base64(),contentId:z20.string().max(MAX_CONTENT_ID_LENGTH).optional()})),Compose=named("Compose",z20.object({to:z20.array(AddressInput).max(MAX_RECIPIENTS).default([]),cc:z20.array(AddressInput).max(MAX_RECIPIENTS).default([]),bcc:z20.array(AddressInput).max(MAX_RECIPIENTS).default([]),replyTo:z20.array(AddressInput).max(MAX_REPLY_TO).default([]),subject:z20.string().max(MAX_SUBJECT_LENGTH).optional(),text:z20.string().max(MAX_BODY_LENGTH).optional(),html:z20.string().max(MAX_BODY_LENGTH).optional(),attachments:z20.array(AttachmentInput).max(MAX_ATTACHMENTS).default([]),inReplyToMessageId:Id.optional(),from:AddressInput.optional(),signature:z20.boolean().optional(),priority:MessagePriority.default("normal")})),ComposeWithTemplate=named("ComposeWithTemplate",Compose.extend({template:Id.optional(),render:z20.object({params:RenderParams.default({})}).optional(),previewText:z20.string().max(MAX_TEMPLATE_PREVIEW_TEXT_LENGTH).optional(),format:TemplateFormat.optional()})),OutboxStatus=named("OutboxStatus",z20.enum(["queued","sending","sent","failed","cancelled"])),OutboxEntry=named("OutboxEntry",z20.object({id:Id,accountId:Id,status:OutboxStatus,reference:z20.string().nullable(),sendAt:Timestamp,attempt:z20.number().int(),lastError:z20.string().nullable(),providerMessageId:ProviderId.nullable(),internetMessageId:z20.string().nullable(),sentAt:Timestamp.nullable(),sentMessageId:Id.nullable(),threadId:ProviderId.nullable(),createdAt:Timestamp,trackOpens:z20.boolean(),trackClicks:z20.boolean(),trackHoneypot:z20.boolean(),humanOpenCount:z20.number().int(),machineOpenCount:z20.number().int(),humanClickCount:z20.number().int(),machineClickCount:z20.number().int(),firstOpenedAt:Timestamp.nullable(),lastOpenedAt:Timestamp.nullable(),mailMergeId:Id.nullable()})),QueuedSend=named("QueuedSend",OutboxEntry.extend({undoUntil:Timestamp.nullable()})),SendTracking=named("SendTracking",z20.object({opens:z20.boolean().optional(),clicks:z20.boolean().optional(),honeypot:z20.boolean().optional()})),SendOptions=named("SendOptions",z20.object({sendAt:QueryTimestamp.optional(),reference:z20.string().max(MAX_SEND_REFERENCE_LENGTH).optional(),tracking:SendTracking.optional()})),ListOption={listId:ListId.optional()},MESSAGE_ID_PATTERN=/^<[^<>\s@]+@[^<>\s@]+>$/,SEND_HEADER_NAMES=["List-Unsubscribe","List-Unsubscribe-Post","List-Id","List-Help","List-Subscribe","List-Post","List-Owner","List-Archive","Auto-Submitted","Precedence","In-Reply-To","References","Feedback-ID","Keywords","Disposition-Notification-To"],ALLOWED_HEADERS=Object.fromEntries(SEND_HEADER_NAMES.map((name)=>[name.toLowerCase(),!0])),REFUSED_X_HEADERS={"x-priority":!0,"x-msmail-priority":!0};function isSendHeaderAllowed(name){let lower=name.toLowerCase();if(Object.hasOwn(ALLOWED_HEADERS,lower))return!0;return lower.startsWith("x-")&&!lower.startsWith("x-workwae-")&&!Object.hasOwn(REFUSED_X_HEADERS,lower)}function hasControlCharacters(value){return/[\x00-\x1f\x7f]/.test(value)}var SendHeaders=named("SendHeaders",z20.record(z20.string().regex(/^[A-Za-z0-9-]{1,76}$/,"Header names are letters, digits and hyphens"),z20.string().min(1).max(MAX_HEADER_VALUE_LENGTH).refine((value)=>!hasControlCharacters(value),"Header values must not contain control characters")).superRefine((headers,ctx)=>{let names=Object.keys(headers);if(names.length>MAX_SEND_HEADERS)ctx.addIssue({code:"custom",message:`At most ${MAX_SEND_HEADERS} headers`});let seen=new Set;for(let name of names){if(!isSendHeaderAllowed(name))ctx.addIssue({code:"custom",path:[name],message:`${name} cannot be set: use an X- header or one of ${SEND_HEADER_NAMES.join(", ")}`});let lower=name.toLowerCase();if(seen.has(lower))ctx.addIssue({code:"custom",path:[name],message:`${name} is given twice`});seen.add(lower)}})),CalendarInvite=named("CalendarInvite",z20.object({method:z20.enum(["PUBLISH","REQUEST","REPLY","ADD","CANCEL","REFRESH","COUNTER","DECLINECOUNTER"]),content:z20.string().max(MAX_CALENDAR_LENGTH).regex(/^\s*BEGIN:VCALENDAR/i,"content must be an iCalendar object (BEGIN:VCALENDAR)")})),SingleSendOptions={messageId:z20.string().max(MAX_MESSAGE_ID_LENGTH).regex(MESSAGE_ID_PATTERN,"messageId must be one <local@domain>").optional(),headers:SendHeaders.optional(),calendar:CalendarInvite.optional(),wait:z20.number().int().min(1).max(MAX_SEND_WAIT_SECONDS).optional()},MailMergeEntry=named("MailMergeEntry",z20.object({to:AddressInput.extend({address:z20.string().max(MAX_EMAIL_LENGTH)}),params:RenderParams.optional(),messageId:z20.string().max(MAX_MESSAGE_ID_LENGTH).optional(),sendAt:QueryTimestamp.optional()})),SendMessageRequest=named("SendMessageRequest",ComposeWithTemplate.extend(SendOptions.shape).extend(ListOption).extend(SingleSendOptions)),MailMergeRequest=named("MailMergeRequest",ComposeWithTemplate.omit({to:!0,cc:!0,bcc:!0,render:!0}).extend(SendOptions.shape).extend({...ListOption,entries:z20.array(MailMergeEntry).min(1).max(MAX_MAIL_MERGE_ENTRIES)}).strict()),MailMergeSuccess=named("MailMergeSuccess",z20.object({success:z20.literal(!0),to:Address,outboxId:Id,messageId:z20.string(),sendAt:Timestamp,undoUntil:Timestamp.nullable()})),MailMergeFailure=named("MailMergeFailure",z20.object({success:z20.literal(!1),to:Address,reason:z20.string(),error:z20.string(),status:z20.number().int()})),MailMergeResult=named("MailMergeResult",z20.discriminatedUnion("success",[MailMergeSuccess,MailMergeFailure])),MailMergeResultList=named("MailMergeResultList",z20.object({id:Id.nullable(),...listFields(MailMergeResult)})),MailMerge=named("MailMerge",z20.object({id:Id,accountId:Id,reference:z20.string().nullable(),entryCount:z20.number().int(),createdAt:Timestamp,counts:z20.object({queued:z20.number().int(),sending:z20.number().int(),sent:z20.number().int(),failed:z20.number().int(),cancelled:z20.number().int()})})),MailMergeIdInput=named("MailMergeIdInput",AccountIdInput.extend({mailMergeId:Id})),MailMergePage=Page(MailMerge),ListMailMergesQuery=named("ListMailMergesQuery",z20.object(Paged)),MailMergeCancelled=named("MailMergeCancelled",z20.object({cancelled:z20.number().int()})),OutboxIdInput=named("OutboxIdInput",AccountIdInput.extend({outboxId:Id})),OutboxEntryPage=Page(OutboxEntry),ListOutboxQuery=named("ListOutboxQuery",z20.object({status:OutboxStatus.optional(),...Paged})),RescheduleSendRequest=named("RescheduleSendRequest",z20.object({sendAt:QueryTimestamp})),ListTrackingEventsQuery=named("ListTrackingEventsQuery",z20.object(Paged)),TrackingEventKind=named("TrackingEventKind",z20.enum(["open","click"])),TrackingActor=named("TrackingActor",z20.enum(["human","proxy","prefetch","scanner","unknown"])),TrackingClient=named("TrackingClient",z20.enum(["gmail_proxy","gmail_web","gmail_app","outlook_web","outlook_desktop","outlook_mobile","apple_mail","apple_mail_privacy","yahoo","aol","thunderbird","proton","fastmail","zoho","icloud_web","webmail","unknown"])),TrackingDevice=named("TrackingDevice",z20.enum(["desktop","mobile","tablet","bot","unknown"])),TrackingOs=named("TrackingOs",z20.enum(["windows","macos","ios","ipados","android","linux","chromeos"])),TrackingBrowser=named("TrackingBrowser",z20.enum(["chrome","edge","firefox","safari","opera","samsung"])),TrackingSecFetch=named("TrackingSecFetch",z20.object({dest:z20.string().nullable(),mode:z20.string().nullable(),site:z20.string().nullable(),user:z20.boolean().nullable()})),TrackingSignals=named("TrackingSignals",z20.object({method:z20.string(),refererHost:z20.string().nullable(),originHost:z20.string().nullable(),client:TrackingClient,device:TrackingDevice,os:TrackingOs.nullable(),browser:TrackingBrowser.nullable(),country:z20.string().nullable(),region:z20.string().nullable(),city:z20.string().nullable(),timezone:z20.string().nullable(),latitude:z20.number().nullable(),longitude:z20.number().nullable(),secondsAfterSend:z20.number().nullable(),secondsSincePrevious:z20.number().nullable(),sequence:z20.number().int(),isConditional:z20.boolean(),isRange:z20.boolean(),isPrefetch:z20.boolean(),secFetch:TrackingSecFetch})),TrackingEvent=named("TrackingEvent",z20.object({id:Id,kind:TrackingEventKind,linkIndex:z20.number().int().nullable(),url:z20.string().nullable(),occurredAt:Timestamp,userAgent:z20.string().nullable(),ip:z20.string().nullable(),actor:TrackingActor,reasons:z20.array(z20.string()),isMachine:z20.boolean(),...TrackingSignals.shape,headers:z20.record(z20.string(),z20.string())})),TrackingEventPage=Page(TrackingEvent);var AccountConnectedData=named("AccountConnectedData",z21.object({email:z21.string(),provider:AccountProvider,cloud:MicrosoftCloud.nullable(),externalRef:z21.string().nullable(),reauthorized:z21.boolean()})),AccountUpdatedField=named("AccountUpdatedField",z21.enum(["status","externalRef","settings","imap","dav","credentials"])),AccountUpdatedData=named("AccountUpdatedData",z21.object({fields:z21.array(AccountUpdatedField)})),AccountDeletedData=named("AccountDeletedData",z21.object({email:z21.string(),externalRef:z21.string().nullable(),purge:z21.boolean()})),AccountReauthRequiredData=named("AccountReauthRequiredData",AccountConnectedData.omit({reauthorized:!0}).extend({reason:z21.string()})),AccountInitializedData=named("AccountInitializedData",z21.object({mailboxes:z21.number().int(),messages:z21.number().int()})),AccountPurgedData=named("AccountPurgedData",z21.object({accountId:Id,externalRef:z21.string().nullable()})),MailboxData=named("MailboxData",z21.object({mailbox:Mailbox})),MailboxUpdatedData=named("MailboxUpdatedData",MailboxData.extend({changes:z21.array(MailboxChangedField)})),MailboxRef=named("MailboxRef",Mailbox.pick({id:!0,name:!0,role:!0})),MessageData=named("MessageData",z21.object({message:MessageSummary,mailboxes:z21.array(MailboxRef)})),MessageUpdatedData=named("MessageUpdatedData",MessageData.extend({changes:z21.array(MessageChangedField)})),MessageDeletedData=named("MessageDeletedData",MessageData.extend({messageId:Id,providerMessageId:ProviderId})),MessageClassifiedData=named("MessageClassifiedData",MessageData.extend({classification:MessageClassification})),BounceRecipient=named("BounceRecipient",z21.object({address:z21.string(),status:z21.string().nullable(),diagnostic:z21.string().nullable()})),MessageBouncedData=named("MessageBouncedData",z21.object({messageId:Id,outboxId:Id.nullable(),reference:z21.string().nullable(),originalMessageId:z21.string().nullable(),recipients:z21.array(BounceRecipient)})),TrackingHitData=named("TrackingHitData",TrackingEvent.omit({id:!0,kind:!0,linkIndex:!0,ip:!0,headers:!0}).extend({outboxId:Id,reference:z21.string().nullable(),messageId:Id.nullable()})),SendError=named("SendError",z21.object({code:z21.string(),message:z21.string()})),SendFields={outboxId:Id,reference:z21.string().nullable()},RejectedRecipient=named("RejectedRecipient",z21.object({address:z21.string(),status:z21.string().nullable(),diagnostic:z21.string().nullable()})),SendSentData=named("SendSentData",z21.object({...SendFields,attempt:z21.number().int(),providerMessageId:ProviderId.nullable(),internetMessageId:z21.string().nullable(),sentAt:Timestamp,rejectedRecipients:z21.array(RejectedRecipient)})),SendRetryingData=named("SendRetryingData",z21.object({...SendFields,attempt:z21.number().int(),nextAttemptAt:Timestamp,error:SendError})),SendFailedData=named("SendFailedData",z21.object({...SendFields,attempt:z21.number().int(),error:SendError})),SendRescheduledData=named("SendRescheduledData",z21.object({...SendFields,sendAt:Timestamp,previousSendAt:Timestamp})),SendCancelledData=named("SendCancelledData",z21.object({...SendFields,mailMergeId:Id.nullable()})),SuppressionCreatedData=named("SuppressionCreatedData",z21.object({listId:z21.string(),address:z21.string(),source:SuppressionSource})),CalendarInitializedData=named("CalendarInitializedData",z21.object({calendars:z21.number().int(),events:z21.number().int()})),CalendarData=named("CalendarData",z21.object({calendar:Calendar})),CalendarUpdatedData=named("CalendarUpdatedData",CalendarData.extend({changes:z21.array(CalendarChangedField)})),CalendarEventData=named("CalendarEventData",z21.object({event:CalendarEvent})),CalendarEventUpdatedData=named("CalendarEventUpdatedData",CalendarEventData.extend({changes:z21.array(CalendarEventChangedField)})),CalendarEventDeletedData=named("CalendarEventDeletedData",z21.object({eventId:Id,calendarId:Id,kind:EventKind,seriesId:Id.nullable()})),ContactsInitializedData=named("ContactsInitializedData",z21.object({contacts:z21.number().int()})),ContactData=named("ContactData",z21.object({contact:Contact})),ContactUpdatedData=named("ContactUpdatedData",ContactData.extend({changes:z21.array(ContactChangedField)})),ContactDeletedData=named("ContactDeletedData",z21.object({contactId:Id,folderId:Id.nullable()})),WebhookTestData=named("WebhookTestData",z21.object({endpointId:Id})),ExportCompletedData=named("ExportCompletedData",z21.object({exportId:Id,bytes:z21.number().int(),sha256:z21.string(),expiresAt:Timestamp})),ExportFailedData=named("ExportFailedData",z21.object({exportId:Id,error:z21.string()})),MeetingConnectionConnectedData=named("MeetingConnectionConnectedData",z21.object({service:MeetingService,email:z21.string(),reconnected:z21.boolean()})),MeetingConnectionReauthRequiredData=named("MeetingConnectionReauthRequiredData",MeetingConnectionConnectedData.omit({reconnected:!0}).extend({reason:z21.string()})),BookingEventData=named("BookingEventData",z21.object({bookingId:Id,linkId:Id,eventId:Id.nullable(),start:Timestamp,end:Timestamp,invitee:z21.string()}));function defineEvent(scope,data,description){return{scope,data,description}}var EVENTS={"account.connected":defineEvent("account",AccountConnectedData,"An account was connected, or an existing account signed in again (reauthorized)."),"account.updated":defineEvent("account",AccountUpdatedData,"An account's status, externalRef, settings or connection changed."),"account.deleted":defineEvent("account",AccountDeletedData,"An account was deleted; its data is purged after the retention period, or at once with purge."),"account.reauth_required":defineEvent("account",AccountReauthRequiredData,"The provider refused the account's grant: the account must sign in again."),"account.initialized":defineEvent("account",AccountInitializedData,"The account's first mail sync finished; nothing before it is announced message by message."),"account.purged":defineEvent("tenant",AccountPurgedData,"A deleted account's data is gone for good (retention, or an immediate purge). The event names no account, since it outlives the account it reports on."),"mailbox.created":defineEvent("account",MailboxData,"A mailbox (folder or label) appeared."),"mailbox.updated":defineEvent("account",MailboxUpdatedData,"A mailbox was renamed, moved or given another role. Counts change silently."),"mailbox.deleted":defineEvent("account",MailboxData,"A mailbox went away."),"message.created":defineEvent("account",MessageData,"A message was indexed: received, sent, drafted or moved in from outside the sync window."),"message.updated":defineEvent("account",MessageUpdatedData,"A message changed: read, flagged, moved, or linked to the outbox entry it is a copy of."),"message.deleted":defineEvent("account",MessageDeletedData,"A message was removed; carries it and its mailboxes as last indexed."),"message.bounced":defineEvent("account",MessageBouncedData,"A delivery status notification reporting failed recipients reached the account; carries the outbox id when it answers one of our sends."),"message.classified":defineEvent("account",MessageClassifiedData,"AI classified a reply to one of our sends (tenant setting aiReplyClassification): carries the message, its mailboxes and the classification."),"message.opened":defineEvent("account",TrackingHitData,"A recipient's client (or a proxy fetching for it) loaded the open-tracking pixel of a send. Carries the actor, its reasons and the derived signals (client, device, location, referer host, sec-fetch tuple, timing); the raw headers are only on the tracking list."),"message.clicked":defineEvent("account",TrackingHitData,"A tracked link of a send was followed; carries the same as message.opened."),"send.sent":defineEvent("account",SendSentData,"The provider accepted a queued send."),"send.retrying":defineEvent("account",SendRetryingData,"An attempt failed and the send is queued again for nextAttemptAt."),"send.failed":defineEvent("account",SendFailedData,"The send failed for good: refused, or out of attempts."),"send.rescheduled":defineEvent("account",SendRescheduledData,"A queued send got a new sendAt before its first attempt."),"send.cancelled":defineEvent("account",SendCancelledData,"A queued send was cancelled before it went out, on its own or with its mail merge."),"suppression.created":defineEvent("account",SuppressionCreatedData,"An address went onto a suppression list: the recipient unsubscribed (source unsubscribe, listId the send's), a permanent bounce said the address is gone (source bounce, listId global), or a detected opt-out was suppressed, at once or on approval (source reply or complaint, listId the send's or global; source inactive, on approval only, listId global). Only a change is announced; API adds are audited instead. The account is the one whose send it concerns."),"calendar.initialized":defineEvent("account",CalendarInitializedData,"The account's first calendar sync finished; nothing before it is announced event by event."),"calendar.created":defineEvent("account",CalendarData,"A calendar appeared."),"calendar.updated":defineEvent("account",CalendarUpdatedData,"A calendar changed."),"calendar.deleted":defineEvent("account",CalendarData,"A calendar went away."),"calendar_event.created":defineEvent("account",CalendarEventData,"A single event, series or exception appeared. Unmodified occurrences of a series are never announced; an occurrence changed on its own is announced as a new exception."),"calendar_event.updated":defineEvent("account",CalendarEventUpdatedData,"A single event, series or exception changed; a change to a series is one update of the series."),"calendar_event.deleted":defineEvent("account",CalendarEventDeletedData,"A single event, series or exception went away."),"contacts.initialized":defineEvent("account",ContactsInitializedData,"The account's first contacts sync finished; nothing before it is announced contact by contact."),"contact.created":defineEvent("account",ContactData,"A contact appeared."),"contact.updated":defineEvent("account",ContactUpdatedData,"A contact changed."),"contact.deleted":defineEvent("account",ContactDeletedData,"A contact went away."),"export.completed":defineEvent("either",ExportCompletedData,"A data export finished and can be downloaded until data.expiresAt. accountId is null for an export of the whole tenant."),"export.failed":defineEvent("either",ExportFailedData,"A data export failed. accountId is null for an export of the whole tenant."),"meeting_connection.connected":defineEvent("account",MeetingConnectionConnectedData,"An account connected (or reconnected) its own grant at a meeting service (Zoom, Webex)."),"meeting_connection.reauth_required":defineEvent("account",MeetingConnectionReauthRequiredData,"The service refused the account's meeting grant; meetings need a new connect. Mail and calendar sync go on."),"booking.created":defineEvent("account",BookingEventData,"A scheduling link was booked: the account hosts the event, and the invitee is an attendee."),"booking.cancelled":defineEvent("account",BookingEventData,"A booking was cancelled and its event removed."),"webhook.test":defineEvent("tenant",WebhookTestData,"Sent by the test action to the endpoint it names, whatever that endpoint's filters.")},EVENT_TYPES=Object.keys(EVENTS),EventType=named("EventType",z21.enum(EVENT_TYPES)),TEST_EVENT_TYPE="webhook.test";var ENVELOPE_ACCOUNT_ID={account:Id,tenant:z21.null(),either:Id.nullable()};function envelope(type){let{scope,data,description}=EVENTS[type],name=type.replace(/(?:^|[._])([a-z])/g,(_,letter)=>letter.toUpperCase());return named(`${name}Event`,z21.object({id:EventId,type:z21.literal(type),accountId:ENVELOPE_ACCOUNT_ID[scope],createdAt:Timestamp,data,isTest:z21.literal(!0).exactOptional()}).meta({description}))}var Event=named("Event",z21.discriminatedUnion("type",EVENT_TYPES.map(envelope))),ListChangesQuery=named("ListChangesQuery",z21.object({cursor:Cursor.optional(),limit:Limit,types:z21.union([z21.string(),z21.array(EventType)]).transform((v)=>Array.isArray(v)?v:v.split(",")).pipe(z21.array(EventType)).optional()})),EventFeedPage=named("EventFeedPage",z21.object({items:z21.array(Event),nextCursor:Cursor}));import*as z22 from"zod";var ExportStatus=named("ExportStatus",z22.enum(["queued","running","completed","failed","expired"])),DataExport=named("DataExport",z22.object({id:Id,accountId:Id.nullable(),accountEmail:z22.string().nullable(),status:ExportStatus,bytes:z22.number().int(),sha256:z22.string().nullable(),contents:z22.record(z22.string(),z22.number().int()),error:z22.string().nullable(),createdAt:Timestamp,startedAt:Timestamp.nullable(),completedAt:Timestamp.nullable(),expiresAt:Timestamp.nullable()})),DataExportPage=Page(DataExport),CreateDataExport=named("CreateDataExport",z22.object({accountId:Id.optional()})),ListDataExportsQuery=named("ListDataExportsQuery",z22.object(Paged)),DataExportIdInput=named("DataExportIdInput",z22.object({exportId:Id}));import*as z23 from"zod";var IdentitySource=named("IdentitySource",z23.enum(["primary","provider","custom"])),Signature=named("Signature",z23.object({html:z23.string().nullable(),text:z23.string().nullable()})),SignatureInput=named("SignatureInput",z23.object({html:z23.string().max(MAX_SIGNATURE_HTML_LENGTH).optional(),text:z23.string().max(MAX_SIGNATURE_TEXT_LENGTH).optional()}).refine((s)=>s.html!==void 0||s.text!==void 0,{message:"A signature needs html or text"})),Identity=named("Identity",Address.extend({id:Id,source:IdentitySource,signature:Signature.nullable(),providerSignature:Signature.nullable()})),IdentityList=named("IdentityList",z23.object({...listFields(Identity),refreshedAt:Timestamp.nullable()})),MailSettings=named("MailSettings",z23.object({signByDefault:z23.boolean()})),UpdateMailSettings=named("UpdateMailSettings",MailSettings.partial()),IdentityIdInput=named("IdentityIdInput",AccountIdInput.extend({identityId:Id})),ListIdentitiesQuery=named("ListIdentitiesQuery",z23.object({refresh:QueryBoolean.optional()})),CreateIdentity=named("CreateIdentity",AddressInput.extend({signature:SignatureInput.optional()})),UpdateIdentity=named("UpdateIdentity",z23.object({name:z23.string().max(MAX_ADDRESS_NAME_LENGTH).nullable().optional(),signature:SignatureInput.nullable().optional()}));import*as z24 from"zod";var PUBSUB_TOPIC_PATTERN=/^projects\/[a-z][a-z0-9.:-]{4,61}[a-z0-9]\/topics\/(?!goog)[A-Za-z][A-Za-z0-9._~+%-]{2,254}$/,GmailPushSettings=named("GmailPushSettings",z24.object({topic:z24.string().regex(PUBSUB_TOPIC_PATTERN,"Expected projects/<project>/topics/<topic>"),serviceAccount:z24.email().regex(/\.gserviceaccount\.com$/i,"Expected a Google service account email")})),OAuthApp=named("OAuthApp",z24.object({id:Id,provider:OAuthProvider,name:z24.string(),clientId:z24.string(),cloud:MicrosoftCloud.nullable(),authority:z24.string().nullable(),useConnectDomain:z24.boolean(),redirectUri:z24.url(),gmailPush:GmailPushSettings.nullable(),gmailPushEndpoint:z24.url().nullable(),createdAt:Timestamp})),OAuthAppList=List(OAuthApp),CreateOAuthApp=named("CreateOAuthApp",z24.object({provider:OAuthProvider,cloud:MicrosoftCloud.optional(),name:Name,clientId:z24.string().min(1),clientSecret:z24.string().min(1),authority:z24.string().optional(),useConnectDomain:z24.boolean().optional()})),UpdateOAuthApp=named("UpdateOAuthApp",z24.object({name:Name.optional(),clientSecret:z24.string().min(1).optional(),authority:z24.string().min(1).optional(),gmailPush:GmailPushSettings.nullable().optional(),useConnectDomain:z24.boolean().optional()})),OAuthAppIdInput=named("OAuthAppIdInput",z24.object({appId:Id}));import*as z25 from"zod";var OrgConnectionKind=named("OrgConnectionKind",z25.enum(["google_delegation","microsoft_app"])),OrgConnectionStatus=named("OrgConnectionStatus",z25.enum(["pending_consent","unverified","active","failing"])),MicrosoftAppCredential=named("MicrosoftAppCredential",z25.enum(["platform","client_secret","certificate"])),GoogleDelegation=named("GoogleDelegation",z25.object({domain:z25.string(),serviceAccountEmail:z25.string(),directoryAdmin:z25.string().nullable(),scopes:z25.array(z25.string()),groupsEnabled:z25.boolean()})),GraphPermission=named("GraphPermission",z25.object({name:z25.string(),isRequired:z25.boolean()})),MicrosoftAppAccess=named("MicrosoftAppAccess",z25.object({cloud:MicrosoftCloud,credentialType:MicrosoftAppCredential,directoryId:z25.string().nullable(),useConnectDomain:z25.boolean(),redirectUri:z25.string().nullable(),permissions:z25.array(GraphPermission)})),OrgConnection=named("OrgConnection",z25.object({id:Id,kind:OrgConnectionKind,name:z25.string(),clientId:z25.string(),status:OrgConnectionStatus,statusDetail:z25.string().nullable(),checkedAt:Timestamp.nullable(),google:GoogleDelegation.nullable(),microsoft:MicrosoftAppAccess.nullable(),accounts:z25.number().int(),createdAt:Timestamp,updatedAt:Timestamp})),OrgConnectionList=List(OrgConnection),Pem=z25.string().min(1).max(MAX_KEY_FILE_LENGTH),ServiceAccountKey=z25.string().min(2).max(MAX_KEY_FILE_LENGTH),EntraId=z25.string().min(1).max(MAX_ENTRA_ID_LENGTH),ClientSecret2=z25.string().min(1).max(MAX_SECRET_LENGTH),CreateOrgConnection=named("CreateOrgConnection",z25.discriminatedUnion("kind",[z25.object({kind:z25.literal("google_delegation"),name:Name,serviceAccountKey:ServiceAccountKey,domain:z25.string().min(3).max(MAX_HOST_LENGTH),calendarEnabled:z25.boolean().default(!0),contactsEnabled:z25.boolean().default(!1),directoryAdmin:Email.optional(),groupsEnabled:z25.boolean().default(!1)}),z25.object({kind:z25.literal("microsoft_app"),name:Name,cloud:MicrosoftCloud.default("global"),credential:z25.discriminatedUnion("type",[z25.object({type:z25.literal("platform")}),z25.object({type:z25.literal("client_secret"),clientId:EntraId,clientSecret:ClientSecret2}),z25.object({type:z25.literal("certificate"),clientId:EntraId,certificate:Pem,privateKey:Pem})]),directoryId:EntraId.optional(),useConnectDomain:z25.boolean().optional()})])),UpdateOrgConnection=named("UpdateOrgConnection",z25.object({name:Name.optional(),serviceAccountKey:ServiceAccountKey.optional(),calendarEnabled:z25.boolean().optional(),contactsEnabled:z25.boolean().optional(),groupsEnabled:z25.boolean().optional(),directoryAdmin:Email.nullable().optional(),clientSecret:ClientSecret2.optional(),certificate:Pem.optional(),privateKey:Pem.optional(),directoryId:EntraId.optional(),useConnectDomain:z25.boolean().optional()})),OrgConnectionCheck=named("OrgConnectionCheck",z25.object({name:z25.string(),isOk:z25.boolean(),isRequired:z25.boolean(),detail:z25.string()})),OrgConnectionTestResult=named("OrgConnectionTestResult",z25.object({connection:OrgConnection,checks:z25.array(OrgConnectionCheck)})),DirectoryEntry=named("DirectoryEntry",z25.object({id:ProviderId,email:z25.string(),displayName:z25.string().nullable(),accountId:Id.nullable()})),DirectoryEntryPage=Page(DirectoryEntry),AddMailboxInput=named("AddMailboxInput",z25.object({email:Email,displayName:z25.string().max(MAX_DISPLAY_NAME_LENGTH).optional(),externalRef:ExternalRef.optional()})),AddMailboxesRequest=named("AddMailboxesRequest",z25.object({mailboxes:z25.array(AddMailboxInput).min(1).max(MAX_ADD_MAILBOXES),settings:SettingsOverride.optional()})),AddedMailboxStatus=named("AddedMailboxStatus",z25.enum(["created","exists","failed"])),AddedMailboxError=named("AddedMailboxError",z25.object({reason:z25.string(),message:z25.string()})),AddedMailbox=named("AddedMailbox",z25.object({email:z25.string(),status:AddedMailboxStatus,accountId:Id.nullable(),error:AddedMailboxError.nullable()})),AddedMailboxList=List(AddedMailbox),OrgConnectionIdInput=named("OrgConnectionIdInput",z25.object({connectionId:Id})),StartConsentRequest=named("StartConsentRequest",z25.object({redirectUri:z25.url(),state:RedirectState.optional()})),ConsentLink=named("ConsentLink",z25.object({consentUrl:z25.url()})),TestOrgConnectionRequest=named("TestOrgConnectionRequest",z25.object({user:Email.optional()})),ListDirectoryEntriesQuery=named("ListDirectoryEntriesQuery",z25.object({cursor:Cursor.optional(),limit:pageLimit(MAX_DIRECTORY_PAGE_SIZE,DEFAULT_DIRECTORY_PAGE_SIZE),query:z25.string().max(MAX_DIRECTORY_QUERY_LENGTH).optional()})),OpenSharedMailboxRequest=named("OpenSharedMailboxRequest",z25.object({email:Email,displayName:z25.string().max(MAX_DISPLAY_NAME_LENGTH).optional(),externalRef:ExternalRef.optional(),settings:SettingsOverride.optional()})),AddGroupRequest=named("AddGroupRequest",z25.object({email:Email,relayEmail:Email,displayName:z25.string().max(MAX_DISPLAY_NAME_LENGTH).optional(),externalRef:ExternalRef.optional(),settings:SettingsOverride.optional()}));import*as z26 from"zod";var MAIL_SETTINGS_CHECK=errors({login_failed:"A server (IMAP, SMTP, CalDAV or CardDAV) refuses the login.",server_not_allowed:"A server resolves to a private or loopback address.",plaintext_not_allowed:"A server is named with security none (a plaintext login)."}),AUTODISCOVERY_FAILED=errors({autodiscovery_failed:"imap was left out and autodiscovery found no IMAP server for the address."}),DAV_SETTINGS_CHECK=errors({dav_discovery_failed:"Neither a CalDAV nor a CardDAV server was found or answered.",not_dav_server:"A named CalDAV or CardDAV URL is no DAV server (it answers 404, 403, or not as one)."}),OAUTH_CLIENT=errors({unknown_oauth_app:"oauthAppId names no OAuth app of the tenant.",oauth_app_mismatch:"The OAuth app is for the other provider.",oauth_app_secret_unreadable:"The OAuth app's client secret cannot be decrypted; replace it.",provider_not_configured:"No OAuth app is given and the platform has none for the provider (and cloud)."}),accounts={list:base.route({method:"GET",path:"/v1/accounts",tags:["Accounts"],summary:"List accounts",description:"Newest first. q matches part of the email address or the external reference, case-insensitively; externalRef matches it exactly. An API key bound to accounts lists only those."}).errors(INVALID_CURSOR).input(ListAccountsQuery).output(AccountPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}",tags:["Accounts"],summary:"Get an account"}).errors(ACCOUNT_NOT_FOUND).input(AccountIdInput).output(Account),create:base.route({method:"POST",path:"/v1/accounts",tags:["Accounts"],summary:"Connect an account from an existing refresh token",description:"The address is the provider's (Google's userinfo, Graph's /me). The token must cover the calendar and contacts that settings.calendarEnabled and settings.contactsEnabled name as true. Where settings leaves one out and the workspace default is on, a Google token that already carries its scopes gets it, and any other token connects with it off for this account (a Microsoft token is refreshed only for the scopes asked, so name the setting to require them).",successStatus:201}).errors({...bodyStorage("settings.bodyStorage"),...planModule("shared mailboxes (sharedMailboxesEnabled: true)"),...PLAN_LIMIT_ACCOUNTS,...BILLING_PAST_DUE,...OAUTH_CLIENT,...errors({invalid_grant:"The provider refuses the refresh token (revoked, expired or never valid).",invalid_client:"The provider refuses the OAuth app's client credentials.",interaction_required:"The provider needs the user to sign in again (new MFA or Conditional Access requirement, or consent).",token_exchange_failed:"The provider's token endpoint refuses the refresh for another reason.",scope_denied:"The grant lacks a scope the account needs.",access_token_rejected:"The provider refuses the access token the refresh returned.",identity_failed:"The provider's identity answer names no address.",identity_unavailable:"Google has not verified the account's address.",provider_unavailable:"The provider's token or identity endpoint could not be reached.",mail_permissions_unsupported:"mailPermissions is read_send or read with sharedMailboxesEnabled: shared mailbox grants are full mail access only."})}).input(CreateAccount).output(Account),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}",tags:["Accounts"],summary:"Update an account",description:"Changing settings.bodyStorage, mailEnabled, calendarEnabled, contactsEnabled or syncWindowDays restarts the account's syncs: full stores every indexed message and backfills, none deletes stored content. status only moves between active and paused; only a new grant clears reauth_required and disabled."}).errors({...ACCOUNT_NOT_FOUND,...PLAN_LIMIT_ACCOUNTS,...bodyStorage("settings.bodyStorage"),...errors({account_reauth_required:"status is changed on an account that must be reconnected.",account_disabled:"status is changed on a disabled account."})}).input(AccountIdInput.extend(UpdateAccount.shape)).output(Account),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}",tags:["Accounts"],summary:"Delete an account",description:"Stops syncing and removes the credential at once. The account's data is purged for good after the retention period (default 30 days), or shortly after the call with ?purge=true; account.purged announces it. Its events are left to age out of the event log (within 31 days). purge=true also purges an account that is already deleted.",successStatus:204,inputStructure:"detailed"}).errors(errors({account_not_found:"The account does not exist, or (without purge) is already deleted."})).input(DeleteAccountInput).output(z26.void()),sync:base.route({method:"POST",path:"/v1/accounts/{accountId}/sync",tags:["Accounts"],summary:"Sync an account now",description:"Wakes the account's sync the way a provider push does: one nudge per kind to the instance that owns it. The owner keeps at most one pending pass per kind, so a repeat while a pass is running becomes a single extra pass. At most 30 of these requests per account per minute (429 rate_limited). kinds defaults to every kind the account has enabled (each of mail, calendar and contacts its settings turn on). With no live owner the answer is still 202: the next owner starts with a full pass, and the backstop poll covers a wake nobody was listening for.",successStatus:202}).errors({...ACCOUNT_NOT_FOUND,...errors({account_reauth_required:"The account must be reconnected before it can sync.",account_disabled:"The account is disabled.",sync_kinds_disabled:"kinds names a sync the account does not run; data.kinds lists those."})}).input(AccountIdInput.extend(SyncAccountRequest.shape)).output(AccountSyncQueued),mintToken:base.route({method:"POST",path:"/v1/accounts/{accountId}/tokens",tags:["Accounts"],summary:"Mint an account-scoped token",description:"The token is returned once; it is not retrievable later."}).errors(ACCOUNT_NOT_FOUND).input(AccountIdInput.extend(MintAccountTokenRequest.shape)).output(AccountToken)},imapAccounts={create:base.route({method:"POST",path:"/v1/imap-accounts",tags:["Accounts"],summary:"Connect an IMAP/SMTP account with a password",description:"The login is checked before anything is stored, for DAV too. Giving dav switches settings.calendarEnabled and contactsEnabled on for the halves found unless settings names them.",successStatus:201}).errors({...MAIL_SETTINGS_CHECK,...AUTODISCOVERY_FAILED,...DAV_SETTINGS_CHECK,...PROVIDER_FAILURES,...bodyStorage("settings.bodyStorage"),...PLAN_LIMIT_ACCOUNTS,...BILLING_PAST_DUE,...planModule("mail (an IMAP account is a mail account)")}).input(CreateImapAccount).output(Account)},imapSettings={update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/imap-settings",tags:["Accounts"],summary:"Change an IMAP account's servers or password",description:"Present fields replace the stored ones and the result is checked like a new account. An account in reauth_required becomes active again. A stored password is never sent to a new server or tried for another login: changing the IMAP server (host, port, security or user) needs password in the same request, changing the SMTP server (host, port, security or user) needs smtpPassword (or password when SMTP uses the IMAP password), switching SMTP from its own password to the IMAP one (smtpPassword: null) needs password, and adding DAV settings or changing their URLs, user or password source needs the password DAV logs in with (dav.password, or password when DAV uses the IMAP password). Sending dav: null removes the calendar and contacts; a CalDAV or CardDAV half the account did not have before switches settings.calendarEnabled or contactsEnabled on, unless the account's settings already name that key (an earlier opt-out stays); dav fields left out keep their stored values, or are discovered for an account without DAV settings. An account that signs in with OAuth always uses its provider's servers."}).errors({...ACCOUNT_NOT_FOUND,...MAIL_SETTINGS_CHECK,...DAV_SETTINGS_CHECK,...PROVIDER_FAILURES,...errors({not_imap_account:"The account reads mail through a provider's API, not IMAP.",password_required:"A server, login user or password source changes without its password.",password_not_stored:"The account has no stored password and none is sent.",dav_unsupported:"dav is given for an account that signs in with OAuth.",oauth_servers_fixed:"The servers of an account that signs in with OAuth are changed.",password_unsupported:"A password is sent for an account that signs in with OAuth.",account_reauth_required:"The OAuth account's stored grant no longer works, so its servers cannot be checked.",credential_missing:"The OAuth account has no stored credential.",credential_conflict:"The OAuth account's credential kept changing while it was refreshed.",invalid_client:"The provider refuses the OAuth account's client credentials.",token_exchange_failed:"The provider's token endpoint refuses the OAuth account's refresh.",provider_not_configured:"The platform OAuth app the account signed in with is gone.",oauth_app_secret_unreadable:"The client secret of the account's OAuth app cannot be decrypted; replace it."})}).input(AccountIdInput.extend(UpdateImapSettings.shape)).output(Account)},autoconfig={lookup:base.route({method:"GET",path:"/v1/autoconfig",tags:["Accounts"],summary:"Find IMAP, SMTP and DAV settings for an address",description:"dav carries CalDAV and CardDAV context URLs when a source names them; they need the login to resolve to the account's home sets, which creating the account does."}).errors(errors({autodiscovery_failed:"No IMAP settings were found for the address; ask the user for the servers then."})).input(AutoconfigQuery).output(DiscoveredSettings)};var ai={draft:base.route({method:"POST",path:"/v1/ai/draft",tags:["AI"],summary:"Draft email text with AI",description:"Writes an email body from instructions, optionally answering a message or reworking a draft, in plain text or Markdown. Nothing is sent or saved: the text is for a person to edit. Needs the write scope; drafts are held to a per-minute limit (beyond it, rate_limited with Retry-After). A workspace without a subscription (the free tier) may write only as many drafts a month as its included amount (plan_limit_ai), and one that is not its owner's Free workspace and has no plan may write none (plan_required)."}).errors({...planModule("AI"),...errors({plan_limit_ai:"The workspace has no subscription and has written all the AI drafts its free tier includes this month (UTC).",plan_required:PLAN_REQUIRED_CONDITION,spend_cap_reached:spendCapCondition("a draft goes beyond the AI drafts the plan includes")}),...accountState("The account of the message being answered"),...PROVIDER_FAILURES,...errors({ai_disabled:"AI drafting is unavailable.",unknown_account:"context.accountId names an account the tenant does not have; data.ids lists it.",unknown_message:"context.replyToMessageId names a message the account does not have.",mail_disabled:"The account of the message being answered has its mail off.",provider_not_found:"The provider no longer has the message being answered.",provider_forbidden:"The provider refused to read the message being answered.",ai_refused:"The model declines the instructions.",ai_unavailable:"The AI service fails or does not answer in time.",ai_suspended:"The AI service refuses Workwae's requests for now; try again later."})}).input(CreateAiDraft).output(AiDraft)};var auditLog={list:base.route({method:"GET",path:"/v1/audit-log",tags:["Audit log"],summary:"Read the audit log",description:"Every mutating request on /v1 and from the console, newest first. Needs the admin scope. action matches exactly, or as a prefix when it ends with a dot (accounts.). outcome success is a 2xx status."}).errors({...INVALID_CURSOR,...errors({invalid_range:"to is before from."})}).input(ListAuditLogQuery).output(AuditEntryPage)};import*as z27 from"zod";var CALENDAR_ACCOUNT={...planModule("calendar"),...ACCOUNT_NOT_FOUND,...errors({calendar_disabled:"The account's calendar sync is off, or it is an IMAP account without CalDAV settings."})},CALENDAR_PROVIDER={...accountState(),...PROVIDER_FAILURES,...errors({provider_forbidden:"The provider refused the account access (e.g. the grant lacks calendar access).",provider_not_found:"The event or calendar is gone at the provider.",provider_unsupported:"The provider cannot express the request (e.g. a rule Microsoft has no pattern for), or an IMAP account whose CalDAV server does not schedule has no SMTP server to mail the iTIP message from.",provider_rejected:`The provider refused the request, or more than ${MAX_EVENT_ATTENDEES} attendees would be mailed an iTIP message.`})},CALENDAR_WRITE={...CALENDAR_PROVIDER,...errors({provider_conflict:"The event changed at the provider meanwhile.",calendar_read_only:"The account can only read the event's calendar."})},EVENT_TIMES=errors({invalid_time_zone:"A timed start or end names an unknown time zone.",mixed_event_times:"One end is timed and the other all-day.",invalid_range:"end is not after start.",invalid_recurrence:"recurrence has no RRULE or RDATE line, or does not parse as RFC 5545."}),EVENT_NOT_FOUND=errors({calendar_event_not_found:"The event does not exist in the account's synced calendars."}),MEETING_SERVICE=errors({meeting_service_not_connected:"The account has no connection to the service and the tenant no server app it may use (a password IMAP account may not use one).",meeting_service_reauth_required:"The account's connection or the tenant's server app was refused and must be connected or fixed again.",meeting_recurrence_unsupported:"The service cannot repeat a meeting by the event's rule.",meeting_service_rejected:"The service refused the meeting.",meeting_service_unavailable:"The service could not be reached."}),calendars={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/calendars",tags:["Calendar"],summary:"List calendars"}).errors(CALENDAR_ACCOUNT).input(AccountIdInput).output(CalendarList),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/calendars",tags:["Calendar"],summary:"Create a calendar",description:"Microsoft stores the name and, when given, color (#rrggbb); a description or time zone is provider_unsupported (the mailbox zone applies to every calendar). Google stores the name, description, time zone and color. CalDAV stores the name, description and color (Apple calendar-color); a time zone is provider_unsupported.",successStatus:201}).errors({...CALENDAR_ACCOUNT,...CALENDAR_WRITE}).input(CreateCalendarInput).output(Calendar),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/calendars/{calendarId}",tags:["Calendar"],summary:"Rename a calendar or change its color",description:"Same fields as create. The primary calendar can be renamed where the provider allows it. A calendar the account cannot write is calendar_read_only."}).errors({...CALENDAR_ACCOUNT,...errors({calendar_not_found:"The calendar does not exist."}),...CALENDAR_WRITE}).input(CalendarIdInput.extend(UpdateCalendar.shape)).output(Calendar),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/calendars/{calendarId}",tags:["Calendar"],summary:"Delete a calendar",description:"Refused for the primary calendar (calendar_primary). Events in it leave the index with the calendar and are not announced one by one, as when a sync sees the calendar gone. A provider that still holds events deletes them with the calendar.",successStatus:204}).errors({...CALENDAR_ACCOUNT,...errors({calendar_not_found:"The calendar does not exist.",calendar_primary:"The account's primary calendar cannot be deleted."}),...CALENDAR_WRITE}).input(CalendarIdInput).output(z27.void())},calendarEvents={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/calendar-events",tags:["Calendar"],summary:"List events in a time range, by start",description:"Returns singles, occurrences and exceptions overlapping [start, end), not series definitions. Only the sync window (by default 90 days back to 365 days ahead) is indexed."}).errors({...CALENDAR_ACCOUNT,...INVALID_CURSOR,...errors({invalid_range:`end is not after start, or the range spans more than ${MAX_EVENT_RANGE_DAYS} days.`,unknown_calendar:"calendarId names no synced calendar of the account."})}).input(ListCalendarEventsInput).output(CalendarEventPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/calendar-events/{eventId}",tags:["Calendar"],summary:"Get an event with its body",description:"Works for every kind, including a series (its recurrence rule)."}).errors({...CALENDAR_ACCOUNT,...EVENT_NOT_FOUND,...CALENDAR_PROVIDER}).input(CalendarEventIdInput).output(CalendarEventDetail),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/calendar-events",tags:["Calendar"],summary:"Create an event or a recurring series",description:"onlineMeeting asks the provider for a Teams (Microsoft) or Meet (Google) link; onlineMeetingUrl attaches a link from another service (Zoom, Webex, ...), read back as onlineMeetingUrl. On Microsoft that link is the location's link (location defaults to the URL) and a join line is added to a body sent along. onlineMeetingService has Workwae create a Zoom or Webex meeting (recurring for a series) hosted by the account's own grant at the service, else by the tenant's server app as the service user with the mailbox's address, and attaches its join link the same way. The server app hosts only accounts whose address the provider verified (Gmail, Microsoft, IMAP signed in with OAuth): a password IMAP account's address is whatever was typed, so it needs its own grant. The meeting is created only once the account is active, and deleted again if anything fails before its link is saved. On an IMAP account whose CalDAV server does not schedule, the invitation is mailed from the account's SMTP server when notify is true and there are attendees; a missing SMTP server is refused before anything is created, and a send that fails after the event is created is logged and the event stands.",successStatus:201}).errors({...CALENDAR_ACCOUNT,...CALENDAR_WRITE,...EVENT_TIMES,...MEETING_SERVICE,...errors({plan_module_unavailable:"The tenant's plan does not include calendar, or onlineMeetingService is given and it does not include meetings.",calendar_not_found:"calendarId names no synced calendar of the account.",calendar_event_not_found:"The provider no longer has the event when it is read back.",multiple_meeting_options:"More than one of onlineMeeting, onlineMeetingUrl and onlineMeetingService is given.",meeting_host_missing:"The tenant's server app has no user with the mailbox's address."})}).input(CreateCalendarEventInput).output(CalendarEventDetail),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/calendar-events/{eventId}",tags:["Calendar"],summary:"Change an event, one occurrence, or a whole series",description:"Addressing an occurrence changes only that instance; addressing the series changes every occurrence. onlineMeetingUrl replaces a link from another service (as on create); null removes it (on Google, whatever conference the event has; on Microsoft, the location's link, leaving a Teams meeting alone). For an event whose Zoom or Webex meeting Workwae created, a new subject, start, end or recurrence moves that meeting too, and an onlineMeetingUrl other than its own join link deletes it (sending that link back replaces nothing); changing one occurrence leaves the meeting's series as it is. A missing or refused meeting connection is reported before anything changes; a service failure after the event changed is reported with the event changed (repeat the request). On an IMAP account whose CalDAV server does not schedule, an update of a meeting the account organizes is mailed from its SMTP server when it changes start, end, recurrence, location or attendees, to the attendees Workwae has indexed for it and those the request names; a missing SMTP server or too many attendees is refused before anything changes, and a send that fails after the change is logged and the change stands."}).errors({...CALENDAR_ACCOUNT,...EVENT_NOT_FOUND,...CALENDAR_WRITE,...EVENT_TIMES,...MEETING_SERVICE,...errors({recurrence_on_instance:"recurrence is given for an occurrence or exception.",meeting_deleted:"The event's meeting is gone at the service when it is moved."})}).input(CalendarEventIdInput.extend(UpdateCalendarEvent.shape)).output(CalendarEventDetail),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/calendar-events/{eventId}",tags:["Calendar"],summary:"Delete an event, one occurrence, or a whole series",description:"Cancels the meeting for its attendees when the account organizes it. A Zoom or Webex meeting Workwae created for the event (a series, not one occurrence) is deleted after the provider event: a provider refusal leaves both, and once the event is gone a meeting the service cannot delete (connection gone or refused, service unreachable) is logged and stays at the service while the request still succeeds. On an IMAP account whose CalDAV server does not schedule, the cancellation is mailed from the account's SMTP server to the attendees Workwae has indexed; a missing SMTP server or too many attendees is refused before anything is deleted, and a send that fails after the deletion is logged.",successStatus:204,inputStructure:"detailed"}).errors({...CALENDAR_ACCOUNT,...EVENT_NOT_FOUND,...CALENDAR_WRITE}).input(DeleteCalendarEventInput).output(z27.void()),respond:base.route({method:"POST",path:"/v1/accounts/{accountId}/calendar-events/{eventId}/respond",tags:["Calendar"],summary:"Accept, tentatively accept or decline an invitation",description:"Addressing an occurrence answers that instance; the series answers all of them. On an IMAP account whose CalDAV server does not schedule, the answer is mailed to the organizer from the account's SMTP server; a missing SMTP server is refused before anything changes, and a send that fails after the answer is recorded is logged."}).errors({...CALENDAR_ACCOUNT,...EVENT_NOT_FOUND,...CALENDAR_PROVIDER,...errors({provider_conflict:"The event changed at the provider meanwhile.",organizer_cannot_respond:"The account organizes the event, so there is no invitation to answer."})}).input(CalendarEventIdInput.extend(RespondToCalendarEventRequest.shape)).output(CalendarEvent)},AVAILABILITY_INPUT=errors({invalid_range:`end is not after start, or the range spans more than ${MAX_AVAILABILITY_DAYS} days.`,invalid_time_zone:"timeZone is unknown.",invalid_working_hours:"workingHours does not end after it starts."}),availability={find:base.route({method:"POST",path:"/v1/accounts/{accountId}/availability/find",tags:["Calendar"],summary:"Find busy times and common free slots",description:"A read sent as a POST, the question not fitting a URL. One provider request covers every attendee (Microsoft getSchedule, Google freeBusy.query); the answer is cached briefly per account, attendees and range. An attendee whose free/busy cannot be read (unknown address, calendar not shared) is listed in unresolved and complete is false; by default such an attendee does not constrain the suggestions, and with requireAll no suggestions are made (withheldReason unresolved_attendees)."}).errors({...CALENDAR_ACCOUNT,...CALENDAR_PROVIDER,...AVAILABILITY_INPUT}).input(FindAvailabilityInput).output(Availability),findForAccounts:base.route({method:"POST",path:"/v1/availability/find",tags:["Calendar"],summary:"Find free slots across several accounts",description:`Reads up to ${MAX_AVAILABILITY_ACCOUNTS} of the tenant's accounts, each through its own calendar, and suggests slots where all of them are free (mode all) or at least one is (mode any, for round-robin), with the accounts free for each slot. Other attendees are looked up through the first account and must all be free in either mode. An account that cannot be read (calendar sync off, paused, needing a new sign-in, a provider failure) is listed in unresolved with its error: in mode all it does not constrain the suggestions, in mode any it is never offered as free; requireAll withholds every suggestion instead (withheldReason unresolved_attendees). A read sent as a POST, like the single-account question, and not open to credentials limited to specific accounts.`}).errors({...planModule("calendar"),...AVAILABILITY_INPUT,...errors({unknown_account:"accountIds names an account the tenant does not have (data lists them)."})}).input(FindAccountsAvailabilityInput).output(AccountsAvailability)};var MAIL_CONNECT_MODULES="mail (options.mailAccess imap) or shared mailboxes (options.sharedMailboxesEnabled: true)",CONNECT_REQUEST=errors({redirect_uri_not_allowed:"redirectUri is not on the tenant allowlist (settings.redirectUris).",billing_past_due:"accountId is not given and the tenant's payment is past due beyond the grace period.",unknown_account:"accountId names no live account of the tenant; data.ids names it.",org_connection_account:"accountId names an account that authenticates through an organization connection (test the connection to restore it).",shared_mailbox_account:"accountId names a shared mailbox (reconnect the account that opened it).",mail_permissions_unsupported:"options.mailPermissions is read_send or read (given, or kept by the account being reconnected) with mailAccess imap, sharedMailboxesEnabled, or a session offering the IMAP form: those grants are full mail access only."}),CONNECT_SESSION_NOT_FOUND=errors({connect_session_not_found:"No session has this id (or token)."}),HOSTED_IMAP_SESSION={...CONNECT_SESSION_NOT_FOUND,...errors({session_used:"The session already completed or failed.",session_expired:"The session is past expiresAt.",redirect_uri_not_allowed:"The session's return address is no longer on the tenant allowlist.",provider_not_allowed:"The session does not offer the IMAP form."})},connectSessions={create:base.route({method:"POST",path:"/v1/connect-sessions",tags:["Connect"],summary:"Create a hosted connect session",description:"Returns a link to the hosted connect page. Its OAuth sign-ins pass a Continue page naming the tenant and provider, on the API origin or, for a tenant OAuth app that uses the workspace's connect domain (useConnectDomain), on that domain, and must finish in the browser that pressed Continue (an HttpOnly cookie on that origin); a callback in any other browser renders an error page and connects nothing. A sign-in through an app whose connect domain is not active returns to redirectUri with error=connect_domain_inactive. Without redirectUri the owner ends on Workwae's own confirmation page, which needs no allowlist entry; follow the outcome with GET /v1/connect-sessions/{sessionId} or the account.connected event. A failed sign-in returns with status=error and error set to a ConnectErrorCode.",successStatus:201}).errors({...CONNECT_REQUEST,...bodyStorage("options.bodyStorage"),...planModule(MAIL_CONNECT_MODULES),...errors({unknown_oauth_app:"oauthAppIds names no OAuth app of the tenant.",oauth_app_mismatch:"An OAuth app in oauthAppIds is for the other provider.",account_mismatch:"providers leaves out the sign-in the account reconnects with.",google_imap_requires_own_app:"providers includes google with options.mailAccess imap (given, or kept by the account being reconnected) and no Google app (oauthAppIds, or the reconnected account's own): Google over IMAP needs the tenant's own Google OAuth app.",mcp_key_limit:"mcp is given with accountId, and that account already has as many live MCP keys as it may."})}).input(CreateConnectSession).output(SessionLink),get:base.route({method:"GET",path:"/v1/connect-sessions/{sessionId}",tags:["Connect"],summary:"Read a hosted connect session's outcome"}).errors(CONNECT_SESSION_NOT_FOUND).input(ConnectSessionIdInput).output(ConnectSession)},oauthAuthorizations={create:base.route({method:"POST",path:"/v1/oauth-authorizations",tags:["Connect"],summary:"Start a headless OAuth connect",description:"authorizeUrl is a page naming the tenant and the provider, on the API origin or, for a tenant OAuth app that uses the workspace's connect domain (useConnectDomain), on that domain; only its Continue button (a form POST, accepted only from that page's origin or the console's) leads on to the provider. Continuing works once and binds the sign-in to that browser with an HttpOnly cookie scoped to the callback; a callback arriving in any other browser renders an error page and connects nothing, so send the user's own browser to it. A failed sign-in returns with status=error and error set to a ConnectErrorCode."}).errors({...CONNECT_REQUEST,...bodyStorage("options.bodyStorage"),...planModule(MAIL_CONNECT_MODULES),...OAUTH_CLIENT,...errors({connect_domain_inactive:"The OAuth app (oauthAppId, or the reconnected account's own) uses the workspace's connect domain and it is not active.",connect_grants_nothing:"The plan's modules and the settings (the tenant's, the account's on a reconnect, and options) leave no mail, calendar or contacts for the grant to cover.",account_mismatch:"The account given as accountId signs in with another provider, or with a password.",google_imap_requires_own_app:"provider google with options.mailAccess imap (given, or kept by the account being reconnected) and no Google app (oauthAppId, or the reconnected account's own): Google over IMAP needs the tenant's own Google OAuth app."})}).input(StartOAuthRequest).output(OAuthAuthorization)},HOSTED_TAGS=["Hosted pages"],hostedConnectSessions={get:base.route({method:"GET",path:"/v1/hosted/connect-sessions/{token}",tags:HOSTED_TAGS,summary:"Read a hosted connect session",spec:publicOperation}).errors(CONNECT_SESSION_NOT_FOUND).input(SessionTokenInput).output(ConnectSessionPublic),createImapAccount:base.route({method:"POST",path:"/v1/hosted/connect-sessions/{token}/imap-accounts",tags:HOSTED_TAGS,summary:"Connect an IMAP account from the hosted page",description:"When the tenant's plan allows no more accounts, or its payment is past due beyond the grace period, the session fails and redirectUrl carries status=error&error=plan_limit_accounts (or billing_past_due), as the OAuth callback does. Giving dav switches settings.calendarEnabled and contactsEnabled on for the halves found unless the session's options name them, as POST /v1/imap-accounts does. A reconnect session replaces the password only: the account keeps its stored servers and DAV settings, and any sent here are ignored. Its DAV login is checked again: with the new password when DAV uses the IMAP password, otherwise with dav.password when given (which then replaces the stored DAV password) or the stored one.",spec:publicOperation}).errors({...HOSTED_IMAP_SESSION,...MAIL_SETTINGS_CHECK,...AUTODISCOVERY_FAILED,...DAV_SETTINGS_CHECK,...PROVIDER_FAILURES,...errors({account_exists:"The tenant already has a live IMAP account with this address and the session was not created to reconnect it (accountId).",account_not_found:"The account a reconnect session was created for was deleted.",account_mismatch:"A reconnect session's account has another address, or is not an IMAP account.",password_required:"A reconnect session's DAV has a password of its own, none is stored and dav.password is left out."})}).input(SessionTokenInput.extend(ImapAccountInput.shape)).output(HostedImapResult),autoconfig:base.route({method:"GET",path:"/v1/hosted/connect-sessions/{token}/autoconfig",tags:HOSTED_TAGS,summary:"Find IMAP and SMTP settings from the hosted page",spec:publicOperation}).errors({...HOSTED_IMAP_SESSION,...errors({autodiscovery_failed:"No IMAP settings were found for the address."})}).input(SessionTokenInput.extend(AutoconfigQuery.shape)).output(DiscoveredSettings)};import*as z28 from"zod";var UNAVAILABLE=errors({connect_domain_unavailable:"The deployment has no Cloudflare for SaaS configured for custom domains."}),NOT_FOUND=errors({connect_domain_not_found:"The workspace has no connect domain."}),EDGE=errors({connect_domain_edge_unavailable:"The edge provider (Cloudflare) refused or could not be reached."}),IN_USE=errors({connect_domain_in_use:"An OAuth app, meeting app or organization connection of the workspace uses the connect domain (useConnectDomain): turn that off first."}),connectDomain={get:base.route({method:"GET",path:"/v1/settings/connect-domain",tags:["Settings"],summary:"Get the workspace's connect domain",description:"The workspace's own host for the browser steps of connecting an account through its own provider apps (the redirect URI to register at Google, Microsoft, Zoom or Webex), with the DNS records to publish and where setting it up stands. Requires the admin scope."}).errors({...UNAVAILABLE,...NOT_FOUND}).output(ConnectDomain),set:base.route({method:"PUT",path:"/v1/settings/connect-domain",tags:["Settings"],summary:"Set the workspace's connect domain",description:"Registers the hostname with the edge and answers the DNS records to publish: a CNAME to the deployment (records), any TXT records the edge asks for (records), and the ownership TXT record that is random to this claim (ownership). The domain is pending until the ownership record is published and the edge reports the hostname and its certificate active (checked every few minutes, or on POST /v1/settings/connect-domain/refresh); only then can an OAuth app, meeting app or organization connection be set to use it (useConnectDomain). The redirect URI such an app registers at its provider is then `https://<hostname>/connect/callback/<provider>`. Replaces the workspace's current connect domain once the new hostname is registered, so a refusal leaves the current one serving; the same hostname again changes nothing, unless it failed. Not available for a different hostname while an app uses the connect domain. Requires the admin scope."}).errors({...UNAVAILABLE,...EDGE,...IN_USE,...errors({connect_domain_not_allowed:"The hostname is one of the deployment's own, or a name the edge cannot take.",connect_domain_taken:"A workspace uses the hostname, as its connect or tracking domain."})}).input(SetConnectDomain).output(ConnectDomain),delete:base.route({method:"DELETE",path:"/v1/settings/connect-domain",tags:["Settings"],summary:"Remove the workspace's connect domain",description:"Removes the hostname from the edge and the workspace. Refused while an OAuth app, meeting app or organization connection uses it. Requires the admin scope.",successStatus:204}).errors({...UNAVAILABLE,...NOT_FOUND,...EDGE,...IN_USE}).output(z28.void()),refresh:base.route({method:"POST",path:"/v1/settings/connect-domain/refresh",tags:["Settings"],summary:"Check the workspace's connect domain now",description:"Reads the hostname's state from the edge now instead of waiting for the next check, and answers the updated domain. Requires the admin scope."}).errors({...UNAVAILABLE,...NOT_FOUND,...EDGE}).output(ConnectDomain)};import*as z29 from"zod";var CONTACTS_ACCOUNT={...planModule("contacts"),...ACCOUNT_NOT_FOUND,...errors({contacts_disabled:"The account's contacts sync is off, or it is an IMAP account without CardDAV settings."})},CONTACT_NOT_FOUND=errors({contact_not_found:"The contact does not exist."}),CONTACTS_PROVIDER={...accountState(),...PROVIDER_FAILURES,...errors({provider_forbidden:"The provider refused the account access (e.g. the grant lacks contacts access).",provider_not_found:"The contact is gone at the provider (the index drops it).",provider_conflict:"The contact changed at the provider meanwhile.",provider_rejected:"The provider refused the request."})},CONTACT_WRITE={...CONTACTS_PROVIDER,...errors({contact_unidentifiable:"The contact would have no name, organization, email address or phone number.",provider_unsupported:'The provider cannot store what was given (a folder on Google, or on Microsoft 365 more than 3 email addresses, more than one mobile number or an "other" number).'})},contactFolders={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/contact-folders",tags:["Contacts"],summary:"List contact folders",description:"Microsoft 365 contact folders, the default folder first; empty for Google accounts."}).errors(CONTACTS_ACCOUNT).input(AccountIdInput).output(ContactFolderList)},contacts={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/contacts",tags:["Contacts"],summary:"List or search contacts, by name",description:"Reads the index. q matches part of any name, the organization or any email address, case-insensitively."}).errors({...CONTACTS_ACCOUNT,...INVALID_CURSOR,...errors({unknown_contact_folder:"folderId names no contact folder of the account."})}).input(AccountIdInput.extend(ListContactsQuery.shape)).output(ContactPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/contacts/{contactId}",tags:["Contacts"],summary:"Get a contact",description:"Reads the index."}).errors({...CONTACTS_ACCOUNT,...CONTACT_NOT_FOUND}).input(ContactIdInput).output(Contact),photo:base.route({method:"GET",path:"/v1/accounts/{accountId}/contacts/{contactId}/photo",tags:["Contacts"],summary:"Download a contact's photo",description:"Fetched from the provider on first request and cached until the photo changes.",spec:fileResponse({mediaType:"image/*",description:"The photo as the provider stores it (image/jpeg, image/png, ...)",disposition:"inline"})}).errors({...CONTACTS_ACCOUNT,...CONTACT_NOT_FOUND,...CONTACTS_PROVIDER,...errors({photo_not_found:"The contact has no photo."})}).input(ContactIdInput).output(z29.file()),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/contacts",tags:["Contacts"],summary:"Create a contact",description:"Written to the provider, then to the index, so it is listed at once.",successStatus:201}).errors({...CONTACTS_ACCOUNT,...CONTACT_WRITE,...errors({unknown_contact_folder:"folderId names no contact folder of the account.",contact_not_found:"The provider's answer could not be read back into the index."})}).input(CreateContactInput).output(Contact),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/contacts/{contactId}",tags:["Contacts"],summary:"Change a contact",description:"Fields present change (null clears); emails and phones replace the whole list."}).errors({...CONTACTS_ACCOUNT,...CONTACT_NOT_FOUND,...CONTACT_WRITE}).input(ContactIdInput.extend(UpdateContact.shape)).output(Contact),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/contacts/{contactId}",tags:["Contacts"],summary:"Delete a contact",successStatus:204}).errors({...CONTACTS_ACCOUNT,...CONTACT_NOT_FOUND,...CONTACTS_PROVIDER}).input(ContactIdInput).output(z29.void())};var emailValidations={create:base.route({method:"POST",path:"/v1/email-validations",tags:["Email validation"],summary:"Validate email addresses",description:"Checks whether each address (up to 10) can receive mail, without sending any: syntax, the domain's mail servers and an SMTP conversation with the recipient's server. Takes seconds per address. An address checked within the deployment's cache period (7 days by default) gets that result back, cached, unless refresh is set or it was unknown. The addresses checked again count toward the plan's emailValidationsPerDay; reused results do not, and a request that does not fit is refused whole. On a billed plan with included validation credits, each address checked again also spends one credit (included credits first, then purchased ones; reused results are free), and a request the balance cannot cover is refused whole with 402 validation_credits_exhausted. A request that fails costs no credits. With suppressInvalid, addresses found invalid go on the global suppression list. Every result is kept in the history (GET /v1/email-validations). Needs the write scope; requests are held to a per-minute limit (beyond it, rate_limited with Retry-After)."}).errors(errors({email_validation_disabled:"Email validation is unavailable.",email_validation_unavailable:"The validation service fails or does not answer in time; no address is suppressed.",email_validation_suspended:"The validation service refuses Workwae's requests for now; try again later.",plan_limit_validations:"The addresses to check again exceed what the plan allows today (UTC); nothing was checked.",plan_required:PLAN_REQUIRED_CONDITION,validation_credits_exhausted:"The tenant has fewer email validation credits than the addresses to check again; data.balance and data.needed say how many; nothing was checked."})).input(CreateEmailValidation).output(EmailValidation),list:base.route({method:"GET",path:"/v1/email-validations",tags:["Email validation"],summary:"List earlier validation results",description:"One item per address a validation request answered, newest first, kept for the deployment's retention period (90 days by default). address keeps those containing it, reachability those with that verdict. Readable whether or not the deployment validates."}).errors(INVALID_CURSOR).input(ListEmailValidationsQuery).output(EmailValidationRecordPage)};var changes={list:base.route({method:"GET",path:"/v1/changes",tags:["Changes"],summary:"Read the change feed",description:"Events oldest first. Pass nextCursor back as cursor to continue; it is never null, since the feed has no end, only a present: an empty page means caught up, so poll again from the same cursor later. An API key bound to accounts reads only their events, without the tenant-level ones (accountId null)."}).errors(INVALID_CURSOR).input(ListChangesQuery).output(EventFeedPage)};import*as z30 from"zod";var EXPORT_NOT_FOUND=errors({export_not_found:"The export does not exist."}),exports={create:base.route({method:"POST",path:"/v1/exports",tags:["Exports"],summary:"Export the tenant or one account",description:"Builds a gzip-compressed tar in the background: accounts without secrets, mailboxes, message metadata (JSON lines), stored message bodies as .eml, calendars and events as JSON and .ics, and for a tenant export its webhooks (without signing secrets), templates and settings. Follow it with GET /v1/exports/{exportId}, or the export.completed and export.failed events. Requires the admin scope.",successStatus:202}).errors(errors({storage_unavailable:"No object store is configured.",unknown_account:"accountId names an account the tenant does not have; data.ids lists it.",export_in_progress:"An export of the same scope (the tenant, or the same account) is queued or running."})).input(CreateDataExport).output(DataExport),list:base.route({method:"GET",path:"/v1/exports",tags:["Exports"],summary:"List exports, newest first",description:"Requires the admin scope."}).errors(INVALID_CURSOR).input(ListDataExportsQuery).output(DataExportPage),get:base.route({method:"GET",path:"/v1/exports/{exportId}",tags:["Exports"],summary:"Get an export",description:"Requires the admin scope."}).errors(EXPORT_NOT_FOUND).input(DataExportIdInput).output(DataExport),download:base.route({method:"GET",path:"/v1/exports/{exportId}/download",tags:["Exports"],summary:"Download an export (application/gzip)",description:"Streams the decrypted .tar.gz; compare it with the export's sha256. Requires the admin scope.",spec:fileResponse({mediaType:"application/gzip",description:"The export as a gzip-compressed tar",disposition:"attachment",headers:{"x-export-sha256":{description:"sha256 hex of the body, the export's sha256",schema:{type:"string"}}}})}).errors({...EXPORT_NOT_FOUND,...errors({export_not_ready:"The export is queued or running.",export_failed:"The export failed.",export_expired:"The export's file was deleted.",storage_unavailable:"No object store is configured.",export_corrupt:"A stored part of the export fails its integrity check."})}).input(DataExportIdInput).output(StreamedFile),delete:base.route({method:"DELETE",path:"/v1/exports/{exportId}",tags:["Exports"],summary:"Delete an export and its file",description:"Requires the admin scope.",successStatus:204}).errors({...EXPORT_NOT_FOUND,...errors({export_running:"The export is being built."})}).input(DataExportIdInput).output(z30.void())};import*as z31 from"zod";var IDENTITY_NOT_FOUND=errors({identity_not_found:"The identity does not exist."}),identities={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/identities",tags:["Identities"],summary:"List the addresses the account may send as",description:"The account's own address, the provider's (Gmail send-as addresses with their Gmail signatures in one users.settings.sendAs request; Microsoft proxy addresses from /me) and custom ones. The provider's list is read when the cached one is a day old, or with refresh=true at most once a minute; a paused or disconnected account serves the cached list."}).errors({...MAIL_ACCOUNT,...errors({account_reauth_required:"Reading the provider's list found that the account must be reconnected."}),...PROVIDER_FAILURES}).input(AccountIdInput.extend(ListIdentitiesQuery.shape)).output(IdentityList),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/identities",tags:["Identities"],summary:"Add a custom identity",description:"For IMAP aliases, and Microsoft mailboxes the account has Send As permission on (Graph cannot list those). Sending as it is checked by the provider when the message goes out.",successStatus:201}).errors({...MAIL_ACCOUNT,...errors({identities_managed_by_provider:"The account is a Gmail account (add send-as addresses in Gmail).",identity_exists:"The address is already an identity of the account."})}).input(AccountIdInput.extend(CreateIdentity.shape)).output(Identity),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/identities/{identityId}",tags:["Identities"],summary:"Change an identity's signature or name",description:"signature: null removes ours (Gmail's own then applies again). Only custom identities take a name; the others carry the provider's."}).errors({...MAIL_ACCOUNT,...IDENTITY_NOT_FOUND,...errors({identity_not_custom:"A name is given for a primary or provider identity."})}).input(IdentityIdInput.extend(UpdateIdentity.shape)).output(Identity),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/identities/{identityId}",tags:["Identities"],summary:"Remove a custom identity",successStatus:204}).errors({...MAIL_ACCOUNT,...IDENTITY_NOT_FOUND,...errors({identity_not_custom:"The identity is the primary or a provider identity (those follow the provider)."})}).input(IdentityIdInput).output(z31.void())},mailSettings={get:base.route({method:"GET",path:"/v1/accounts/{accountId}/mail-settings",tags:["Identities"],summary:"Get the account's mail settings"}).errors(MAIL_ACCOUNT).input(AccountIdInput).output(MailSettings),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/mail-settings",tags:["Identities"],summary:"Update the account's mail settings"}).errors(MAIL_ACCOUNT).input(AccountIdInput.extend(UpdateMailSettings.shape)).output(MailSettings)};import*as z32 from"zod";var UNKNOWN_MAILBOX=errors({unknown_mailbox:"A mailbox id names no live mailbox of the account; data.ids lists them."}),PROVIDER_CHANGE={...PROVIDER_FAILURES,...errors({provider_not_found:"The provider no longer has the item.",provider_forbidden:"The provider refused the change for lack of permission.",provider_rejected:"The provider rejected the change as invalid.",provider_unsupported:"The provider does not support the change.",provider_conflict:"The provider refused the change because of the item's state."})},MESSAGE_READ={...MAIL_ACCOUNT,...errors({message_not_found:"The message does not exist."}),...accountState("The provider is needed and the account"),...PROVIDER_FAILURES,...errors({provider_not_found:"The provider no longer has the message or the attachment.",provider_forbidden:"The provider refused to serve the message."})},LIST_BODIES={...accountState("With include=body, the account"),...PROVIDER_FAILURES,...errors({provider_not_found:"With include=body, the provider no longer has one of the messages.",provider_forbidden:"With include=body, the provider refused to serve one of the messages."})},GROUP_MAILBOX_REFUSAL="A Google Group account has no mailboxes of its own to change.",mailboxes={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/mailboxes",tags:["Mail"],summary:"List mailboxes (folders or labels)"}).errors(MAIL_ACCOUNT).input(AccountIdInput).output(MailboxList),create:base.route({method:"POST",path:"/v1/accounts/{accountId}/mailboxes",tags:["Mail"],summary:"Create a mailbox (folder or label)",description:"Microsoft and IMAP create a folder; Gmail creates a label (nested with '/' in the label name when parentId is set). A folder that is not empty can be deleted later: Microsoft deletes the folder and the messages in it, Gmail removes the label and leaves the messages, IMAP deletes the mailbox and its messages and refuses a mailbox that still has child mailboxes (provider_rejected).",successStatus:201}).errors({...MAIL_ACCOUNT,...MAIL_CHANGE,...UNKNOWN_MAILBOX,...errors({group_unsupported:GROUP_MAILBOX_REFUSAL}),...accountState(),...PROVIDER_CHANGE}).input(CreateMailboxInput).output(Mailbox),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/mailboxes/{mailboxId}",tags:["Mail"],summary:"Rename or move a mailbox",description:"Refused for system and special-use mailboxes (Inbox, Sent, Drafts, Trash, Junk, Archive, Gmail system labels) with mailbox_protected. parentId null moves it to the top level. On Gmail the label's name keeps '/' between a parent label and this one."}).errors({...MAIL_ACCOUNT,...MAIL_CHANGE,...UNKNOWN_MAILBOX,...errors({mailbox_protected:"A system or special-use mailbox cannot be renamed or moved.",mailbox_parent:"parentId is this mailbox or a mailbox nested under it.",group_unsupported:GROUP_MAILBOX_REFUSAL}),...accountState(),...PROVIDER_CHANGE}).input(MailboxIdInput.extend(UpdateMailbox.shape)).output(Mailbox),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/mailboxes/{mailboxId}",tags:["Mail"],summary:"Delete a mailbox",description:"Refused for system and special-use mailboxes (mailbox_protected). Microsoft deletes the folder and the messages in it (child folders are refused, provider_rejected). Gmail deletes the label and takes it off its messages, which stay. IMAP deletes the mailbox and its messages; a mailbox that still has children is refused by the server (provider_rejected). The index drops the mailbox now; messages it held leave on the next sync, the same way a deletion made elsewhere does.",successStatus:204}).errors({...MAIL_ACCOUNT,...MAIL_CHANGE,...errors({unknown_mailbox:"mailboxId names no live mailbox of the account.",mailbox_protected:"A system or special-use mailbox cannot be deleted.",group_unsupported:GROUP_MAILBOX_REFUSAL}),...accountState(),...PROVIDER_CHANGE}).input(MailboxIdInput).output(z32.void())},messages={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/messages",tags:["Mail"],summary:"List messages, newest first",description:"Covers the account's sync window. Without mailboxId, lists every mailbox. importance keeps the messages with that priority; messages indexed before priorities were read (importance null) match none. from and to match part of an address or display name, case-insensitive (to looks at To, Cc and Bcc); after and before bound receivedAt. outboxId and internetMessageId match exactly (internetMessageId as one <id>, angle brackets included) and combine with every other filter. include=body answers MessagePage, with each item the full Message that messages.get returns (add headers for its header fields: include=body,headers), instead of MessageSummaryPage; the page holds 25 messages at most (default 25), a larger limit is refused. Bodies come from stored content when the account stores bodies and from the provider otherwise, a few messages at a time; a message the provider cannot serve fails the whole page with the error messages.get would answer for it."}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR,...UNKNOWN_MAILBOX,...LIST_BODIES,...errors({invalid_range:"before is not after after."})}).input(withListIncludeRules(withDateRange(AccountIdInput.extend(ListMessagesQuery.shape),{from:"after",to:"before"}))).output(z32.union([MessagePage,MessageSummaryPage])),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/messages/{messageId}",tags:["Mail"],summary:"Get a message with its body",description:`With bodyStorage cache or full the body comes from stored content (cache fetches and stores the raw message on a miss), and attachment ids are MIME part numbers ("1.2"); otherwise it is read from the provider. Drafts are always read from the provider. inReplyTo and references come from the message's In-Reply-To and References headers; include=headers adds every top-level header field (headers), which is left out otherwise.`}).errors(MESSAGE_READ).input(GetMessageInput).output(Message),raw:base.route({method:"GET",path:"/v1/accounts/{accountId}/messages/{messageId}/raw",tags:["Mail"],summary:"Download the message as RFC 5322 (message/rfc822)",description:"Served from stored content when the account stores bodies.",spec:fileResponse({mediaType:"message/rfc822",description:"The message as message/rfc822",disposition:"attachment"})}).errors(MESSAGE_READ).input(MessageIdInput).output(StreamedFile),attachment:base.route({method:"GET",path:"/v1/accounts/{accountId}/messages/{messageId}/attachments/{attachmentId}",tags:["Mail"],summary:"Download an attachment",description:"Served from stored content when the account stores bodies; an id the stored message does not have is asked of the provider, which answers provider_not_found when it has no such attachment either.",spec:fileResponse({mediaType:"*/*",description:"The attachment, with the Content-Type it was sent with",disposition:"attachment"})}).errors(MESSAGE_READ).input(AttachmentIdInput).output(StreamedFile),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/messages/{messageId}",tags:["Mail"],summary:"Change flags, move, or change labels",description:"mailboxId moves the message (Microsoft folders, or Gmail: replaces the message's folder-like labels). addMailboxIds/removeMailboxIds apply Gmail labels."}).errors({...MAIL_ACCOUNT,...MAIL_CHANGE,...errors({message_not_found:"The message does not exist.",mailbox_not_label:"addMailboxIds or removeMailboxIds names a mailbox that is not a label."}),...UNKNOWN_MAILBOX,...accountState(),...PROVIDER_CHANGE}).input(MessageIdInput.extend(UpdateMessage.shape)).output(MessageSummary),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/messages/{messageId}",tags:["Mail"],summary:"Delete a message",description:"Moves it to Trash; ?permanent=true deletes it outright.",successStatus:204,inputStructure:"detailed"}).errors({...MAIL_ACCOUNT,...MAIL_CHANGE,...errors({message_not_found:"The message does not exist."}),...accountState(),...PROVIDER_CHANGE}).input(DeleteMessageInput).output(z32.void())},search={messages:base.route({method:"GET",path:"/v1/accounts/{accountId}/search/messages",tags:["Mail"],summary:"Search messages",description:['source=index searches the index with full-text search (q in web search syntax: words, "phrases", OR, -word) over subject, sender and recipient names and addresses, the snippet, and the body text of stored messages (bodyStorage cache or full), best matches first.',"source=provider passes q to the provider unchanged (Gmail search syntax, Microsoft KQL, IMAP SEARCH TEXT), newest first; results the index holds carry their id, others id null.","source=auto (default) asks the provider when the index cannot cover the request: `after` lies before the sync window, or the index found nothing while it does not hold everything (the account has a sync window, or bodyStorage is not full). Otherwise, and whenever the provider cannot be asked (inactive account, rate limit, provider error), the index answers.","from, to, unread, flagged and hasAttachments narrow either source's results as on messages.list; a provider page they narrow can hold fewer than limit results while nextCursor goes on.","The response names the source that answered; nextCursor stays with it.","A provider search of a Gmail account counts against the account's Gmail searches per minute; past them it answers rate_limited with Retry-After."].join(" ")}).errors({...MAIL_ACCOUNT,...errors({invalid_range:"before is not after after."}),...errors({invalid_cursor:"The cursor is invalid, was issued for another account, or is a provider cursor older than an hour."}),...UNKNOWN_MAILBOX,...accountState("With source=provider, the account"),...PROVIDER_FAILURES,...errors({provider_forbidden:"With source=provider, the provider refused the search.",provider_rejected:"With source=provider, the provider rejected q.",provider_unsupported:"With source=provider, the provider cannot run the search."})}).input(withDateRange(AccountIdInput.extend(SearchMessagesQuery.shape),{from:"after",to:"before"})).output(SearchPage)};import*as z33 from"zod";var TAGS=["MCP keys"],mcpKeys={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/mcp-keys",tags:TAGS,summary:"Mint an MCP key for an account's user",description:"The key is returned once; it is not retrievable later. It works only at the MCP server (mcpUrl, sent as `Authorization: Bearer <key>`), only for this account and with these scopes; every other route answers it with mcp_only_key. MCP clients that send a header take it (Claude Code, Cursor, Codex and the like); Claude.ai and ChatGPT custom connectors sign in with OAuth instead. Needs the admin scope and a key that is not bound to accounts. Revoked with the account when it is deleted.",successStatus:201}).errors({...ACCOUNT_NOT_FOUND,...errors({mcp_key_limit:`The account already has ${MAX_MCP_KEYS_PER_ACCOUNT} live MCP keys; revoke one first.`})}).input(AccountIdInput.extend(CreateMcpKey.shape)).output(CreatedMcpKey),list:base.route({method:"GET",path:"/v1/accounts/{accountId}/mcp-keys",tags:TAGS,summary:"List an account's MCP keys",description:"Newest first, without their secrets; revoked keys are left out."}).errors(ACCOUNT_NOT_FOUND).input(AccountIdInput).output(McpKeyList),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/mcp-keys/{keyId}",tags:TAGS,summary:"Revoke an MCP key",description:"The MCP server refuses the key from its next request on.",successStatus:204}).errors({...ACCOUNT_NOT_FOUND,...errors({mcp_key_not_found:"The account has no live MCP key with this id."})}).input(McpKeyIdInput).output(z33.void())};import*as z34 from"zod";var APP_TAGS=["Meeting apps"],CONNECTION_TAGS=["Meeting connections"],MEETINGS=planModule("Zoom and Webex meetings"),APP_NOT_FOUND=errors({meeting_app_not_found:"The app does not exist."}),CONNECT_DOMAIN=errors({connect_domain_inactive:"useConnectDomain is true and the workspace has no active connect domain (settings.connectDomain).",connect_domain_unsupported:"useConnectDomain is true for a server app (zoom_s2s, webex_service_app), which has no sign-in to move."}),meetingApps={list:base.route({method:"GET",path:"/v1/meeting-apps",tags:APP_TAGS,summary:"List meeting apps"}).errors(MEETINGS).output(MeetingAppList),create:base.route({method:"POST",path:"/v1/meeting-apps",tags:APP_TAGS,summary:"Register a meeting app",description:"A sign-in app (zoom_oauth, webex_oauth) is named by meeting connect sessions in place of the platform's; register `redirectUri` at the service. It is on the API host, or on the workspace's connect domain when `useConnectDomain` is set. A server app (zoom_s2s, webex_service_app) starts unverified; test it.",successStatus:201}).errors({...MEETINGS,...CONNECT_DOMAIN,...errors({meeting_app_exists:"The tenant already has a server app for the service."})}).input(CreateMeetingApp).output(MeetingApp),update:base.route({method:"PATCH",path:"/v1/meeting-apps/{appId}",tags:APP_TAGS,summary:"Change a meeting app: its connect domain",description:"useConnectDomain moves a sign-in app's sign-ins to the workspace's connect domain, or back to the API host; register the new `redirectUri` at the service first. A sign-in already started finishes on the host it began on. If the connect domain stops being active while an app uses it, the app's sign-ins fail with connect_domain_inactive until it is active again or the app is moved back."}).errors({...MEETINGS,...APP_NOT_FOUND,...CONNECT_DOMAIN}).input(MeetingAppIdInput.extend(UpdateMeetingApp.shape)).output(MeetingApp),test:base.route({method:"POST",path:"/v1/meeting-apps/{appId}/test",tags:APP_TAGS,summary:"Test a meeting server app",description:"Requests a token the way meetings will and, with `user`, looks that address up as a host; the app becomes active or failing."}).errors({...MEETINGS,...APP_NOT_FOUND,...errors({not_testable:"The app is a sign-in app, which only account connects use."})}).input(MeetingAppIdInput.extend(TestMeetingAppRequest.shape)).output(MeetingAppTestResult),delete:base.route({method:"DELETE",path:"/v1/meeting-apps/{appId}",tags:APP_TAGS,summary:"Delete a meeting app",description:"Meetings already created stay at the service. Allowed without the meetings module, so a tenant can remove an app it no longer uses.",successStatus:204}).errors({...APP_NOT_FOUND,...errors({meeting_app_in_use:"Account connections were issued through the sign-in app."})}).input(MeetingAppIdInput).output(z34.void())},meetingConnections={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/meeting-connections",tags:CONNECTION_TAGS,summary:"List an account's meeting connections"}).errors({...MEETINGS,...ACCOUNT_NOT_FOUND}).input(AccountIdInput).output(MeetingConnectionList),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/meeting-connections/{service}",tags:CONNECTION_TAGS,summary:"Remove a meeting connection",description:"Deletes the grant (and revokes it at Zoom); meetings created with it stay. Later events use the tenant's server app, if any. Allowed without the meetings module, so a tenant can remove a grant it no longer uses.",successStatus:204}).errors({...ACCOUNT_NOT_FOUND,...errors({meeting_connection_not_found:"The account has no connection to the service."})}).input(MeetingConnectionInput).output(z34.void())},meetingConnectSessions={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/meeting-connect-sessions",tags:CONNECTION_TAGS,summary:"Create a hosted meeting connect session",description:"Returns a link to the hosted page where the account's user signs in to the service; the browser then returns to redirectUri with status=success|error, service, error and state, or without redirectUri to Workwae's own confirmation page, which needs no allowlist entry. The sign-in passes a Continue page on the API origin naming the tenant, the service and the account, and must finish in the browser that pressed Continue (an HttpOnly cookie on the API origin); a callback in any other browser renders an error page and connects nothing. A session yields at most one grant: once one sign-in completed it, the others end with error=session_used, and one completing after expiresAt ends with error=session_expired. Connecting again replaces the account's grant for the service."}).errors({...MEETINGS,...ACCOUNT_NOT_FOUND,...errors({redirect_uri_not_allowed:"redirectUri is not on the tenant's allowlist.",unknown_meeting_app:"meetingAppId names no meeting app of the tenant.",meeting_app_mismatch:"meetingAppId is not a sign-in app of the service.",meeting_app_secret_unreadable:"The meetingAppId app's secret cannot be decrypted; register the app again.",provider_not_configured:"No meetingAppId is given and the platform has no app for the service."})}).input(AccountIdInput.extend(CreateMeetingConnectSession.shape)).output(SessionLink)},hostedMeetingConnectSessions={get:base.route({method:"GET",path:"/v1/hosted/meeting-connect-sessions/{token}",tags:["Hosted pages"],summary:"Read a hosted meeting connect session",spec:publicOperation}).errors(errors({meeting_connect_session_not_found:"No session has this token, or its account no longer exists."})).input(SessionTokenInput).output(MeetingConnectSessionPublic)};import*as z35 from"zod";var NOT_FOUND2=errors({oauth_app_not_found:"The OAuth app does not exist."}),CONNECT_DOMAIN2=errors({connect_domain_inactive:"useConnectDomain is true and the workspace has no active connect domain (settings.connectDomain)."}),oauthApps={list:base.route({method:"GET",path:"/v1/oauth-apps",tags:["OAuth apps"],summary:"List tenant OAuth apps"}).output(OAuthAppList),create:base.route({method:"POST",path:"/v1/oauth-apps",tags:["OAuth apps"],summary:"Register a tenant OAuth app",description:"redirectUri is the callback to register at the provider as an authorized redirect URI. It is on the API host, or on the workspace's connect domain when useConnectDomain is set: Google only verifies an app whose redirect URIs are on domains its owner controls, so an app that is to be verified signs in through the workspace's own connect domain.",successStatus:201}).errors({...CONNECT_DOMAIN2,...errors({cloud_required:"provider is microsoft and no cloud is given.",not_microsoft:"cloud or authority is given for a Google app."})}).input(CreateOAuthApp).output(OAuthApp),update:base.route({method:"PATCH",path:"/v1/oauth-apps/{appId}",tags:["OAuth apps"],summary:"Change a tenant OAuth app: name, client secret, authority, connect domain or Gmail push",description:"Accounts on the app use the new secret from their next token refresh. useConnectDomain moves the app's sign-ins to the workspace's connect domain, or back to the API host; register the new redirectUri at the provider first. A sign-in already started finishes on the host it began on. If the connect domain stops being active while an app uses it, the app's sign-ins fail with connect_domain_inactive (headless: 409) until it is active again or the app is moved back. gmailPush (Google apps) makes the app's accounts watch the topic with users.watch instead of being polled; null stops their watches and returns them to polling. Changing it wakes the app's accounts so it applies at once, and saving it again retries a watch Gmail refused."}).errors({...NOT_FOUND2,...CONNECT_DOMAIN2,...errors({not_microsoft:"authority is given for a Google app.",gmail_push_unsupported:"gmailPush is given for a Microsoft app.",push_unavailable:"gmailPush is set and Gmail push is unavailable."})}).input(OAuthAppIdInput.extend(UpdateOAuthApp.shape)).output(OAuthApp),delete:base.route({method:"DELETE",path:"/v1/oauth-apps/{appId}",tags:["OAuth apps"],summary:"Delete a tenant OAuth app",successStatus:204}).errors({...NOT_FOUND2,...errors({oauth_app_in_use:"Live accounts still use the app."})}).input(OAuthAppIdInput).output(z35.void())};import*as z36 from"zod";var NOT_FOUND3=errors({org_connection_not_found:"The organization connection does not exist."}),CONSENT_REQUIRED=errors({consent_required:"No Microsoft directory has consented to the app yet."}),CREDENTIALS=errors({invalid_service_account_key:"serviceAccountKey is not a usable Google service account key.",invalid_certificate:"The certificate or private key is not a usable PEM certificate and unencrypted PKCS#8 RSA key.",directory_id_not_allowed:"A directoryId is given for a platform connection."}),CONNECT_DOMAIN3=errors({connect_domain_inactive:"useConnectDomain is true and the workspace has no active connect domain (settings.connectDomain).",connect_domain_unsupported:"useConnectDomain is true for a Google connection or a platform Microsoft connection, whose sign-in does not run on a tenant's own app."}),TAGS2=["Organization connections"],orgConnections={list:base.route({method:"GET",path:"/v1/org-connections",tags:TAGS2,summary:"List organization connections"}).output(OrgConnectionList),create:base.route({method:"POST",path:"/v1/org-connections",tags:TAGS2,summary:"Register an organization connection",description:"Google: the service account key is checked and sealed; authorize the returned clientId for the returned scopes in the Admin console, then test. Microsoft: a platform connection starts in pending_consent (POST .../consent); a tenant app with a directoryId starts unverified.",successStatus:201}).errors({...CREDENTIALS,...CONNECT_DOMAIN3,...errors({provider_not_configured:"A platform connection names a cloud the platform has no app for."})}).input(CreateOrgConnection).output(OrgConnection),get:base.route({method:"GET",path:"/v1/org-connections/{connectionId}",tags:TAGS2,summary:"Get an organization connection"}).errors(NOT_FOUND3).input(OrgConnectionIdInput).output(OrgConnection),update:base.route({method:"PATCH",path:"/v1/org-connections/{connectionId}",tags:TAGS2,summary:"Update an organization connection",description:"Replacing a credential or the authorized scopes makes the connection unverified until the next test."}).errors({...NOT_FOUND3,...CREDENTIALS,...CONNECT_DOMAIN3,...errors({credential_mismatch:"A field does not apply to this connection's kind or credential type, or a certificate is replaced without its private key."})}).input(OrgConnectionIdInput.extend(UpdateOrgConnection.shape)).output(OrgConnection),delete:base.route({method:"DELETE",path:"/v1/org-connections/{connectionId}",tags:TAGS2,summary:"Delete an organization connection",successStatus:204}).errors({...NOT_FOUND3,...errors({connection_in_use:"Live accounts still authenticate through the connection."})}).input(OrgConnectionIdInput).output(z36.void()),consent:base.route({method:"POST",path:"/v1/org-connections/{connectionId}/consent",tags:TAGS2,summary:"Start Microsoft admin consent",description:"Returns the URL an administrator of the directory grants consent at. Afterwards the browser returns to redirectUri with status=success|error, connection_id, error and state; the connection records the consenting directory and is tested. A tenant app uses /adminconsent, with the redirect URI the connection reports (microsoft.redirectUri): on the workspace's connect domain when microsoft.useConnectDomain is set, in which case a connect domain that is no longer active fails the consent with connect_domain_inactive (409) before it starts. The platform app is consented through a sign-in (authorize with prompt=admin_consent) and records the directory named by the sign-in's ID token; it can only be started from the console, and the console user who started it has to be signed in, in the browser that returns, for the directory to be recorded. Errors on return include access_denied, consent_failed, consent_unverified, directory_mismatch and directory_already_connected (another platform connection holds the directory)."}).errors({...NOT_FOUND3,...errors({connect_domain_inactive:"The connection uses the workspace's connect domain (microsoft.useConnectDomain) and it is not active.",not_microsoft:"The connection is a Google connection.",redirect_uri_not_allowed:"redirectUri is neither on the tenant allowlist (settings.redirectUris) nor the console.",session_required:"A platform connection's consent is started by a caller that is not a console session.",provider_not_configured:"A platform connection's cloud has no platform app (the platform's configuration changed)."})}).input(OrgConnectionIdInput.extend(StartConsentRequest.shape)).output(ConsentLink),test:base.route({method:"POST",path:"/v1/org-connections/{connectionId}/test",tags:TAGS2,summary:"Test an organization connection",description:"Requests a token the way accounts will and reports each check; the connection becomes active or failing. A passing test returns accounts that were moved to reauth_required to active. Google impersonates `user` (default: the directory admin)."}).errors({...NOT_FOUND3,...CONSENT_REQUIRED,...errors({user_required:"A Google connection has neither a user nor a directory admin to test as."})}).input(OrgConnectionIdInput.extend(TestOrgConnectionRequest.shape)).output(OrgConnectionTestResult),directoryEntries:base.route({method:"GET",path:"/v1/org-connections/{connectionId}/directory-entries",tags:TAGS2,summary:"List the directory's mailboxes",description:"Microsoft: Graph /users (needs User.Read.All). Google: the Admin SDK users.list as the directory admin (needs admin.directory.user.readonly). `query` matches the start of names and addresses. A nextCursor is good for an hour, on this connection only."}).errors({...NOT_FOUND3,...CONSENT_REQUIRED,...INVALID_CURSOR,...PROVIDER_FAILURES,...errors({directory_unavailable:"The connection may not list the directory: there is no directory admin, the permission or scope is not granted, or the connection cannot get a token (test it).",provider_rejected:"The provider rejected the listing (e.g. the query).",provider_not_found:"The provider no longer has the directory."})}).input(OrgConnectionIdInput.extend(ListDirectoryEntriesQuery.shape)).output(DirectoryEntryPage),addAccounts:base.route({method:"POST",path:"/v1/org-connections/{connectionId}/accounts",tags:TAGS2,summary:"Add mailboxes as accounts",description:"Each mailbox is checked through the connection before its account is created (Google: a delegated token for it; Microsoft: its inbox). items holds one result per mailbox, in request order. Per-mailbox failures are reported there with the reason the API would answer with, e.g. invalid_grant (no such user), consent_revoked (the scopes or permissions are not granted), provider_forbidden or provider_not_found (no mailbox access), plan_limit_accounts (the plan's account limit was reached part way through; the mailboxes before it are created)."}).errors({...NOT_FOUND3,...CONSENT_REQUIRED,...bodyStorage("settings.bodyStorage"),...BILLING_PAST_DUE}).input(OrgConnectionIdInput.extend(AddMailboxesRequest.shape)).output(AddedMailboxList),addGroup:base.route({method:"POST",path:"/v1/org-connections/{connectionId}/groups",tags:TAGS2,summary:"Add a Google Group as an account",description:"Google has no API to read a group, so the group becomes an account of its own (auth method google_group) that is read from and sent through a member user's mailbox, `relayEmail`, with the connection's delegated token. Only the group's mail in that mailbox is synced (Google Groups list mail and mail sent as the group); the rest of the user's mail is never indexed. The group is added to the relay's send-as addresses so replies go out From the group (same-domain groups are accepted at once). Needs the connection's `groupsEnabled` scope authorized in the Admin console. A group has no calendar or contacts. Re-adding an existing group account re-points it at the given relay. Deleting a group account leaves its send-as alias on the relay's mailbox.",successStatus:201}).errors({...NOT_FOUND3,...planModule("Google Groups"),...bodyStorage("settings.bodyStorage"),...PLAN_LIMIT_ACCOUNTS,...BILLING_PAST_DUE,...PROVIDER_FAILURES,...errors({group_unsupported:"The connection is not a Google domain-wide delegation connection, or the group is the relay's own address.",scope_missing:"The connection lacks the gmail.settings.sharing scope (set groupsEnabled and authorize the scope in the Admin console).",group_relay_inaccessible:"The connection cannot act as the relay: it is not a user of the Workspace, or the scopes are not authorized.",group_alias_unverified:"Gmail did not accept the group as a send-as address of the relay (e.g. a group outside the Workspace domain awaiting verification).",account_exists:"The group address is already an account authenticated another way."})}).input(OrgConnectionIdInput.extend(AddGroupRequest.shape)).output(Account)},sharedMailboxes={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/shared-mailboxes",tags:["Shared mailboxes"],summary:"Open a shared mailbox with an account's grant",description:"Creates (or re-points) an account for a shared mailbox the delegating account's user can access, authenticated with that account's grant: Graph addresses it as /users/{email}; an Exchange IMAP account logs in as user\\shared with XOAUTH2. Gmail has no delegated shared mailboxes: add them through a Google domain-wide delegation connection.",successStatus:201}).errors({...planModule("shared mailboxes"),...bodyStorage("settings.bodyStorage"),...PLAN_LIMIT_ACCOUNTS,...BILLING_PAST_DUE,...accountState("The delegating account"),...PROVIDER_FAILURES,...errors({account_not_found:"The delegating account does not exist.",shared_mailbox_unsupported:"The delegating account is Gmail, password IMAP, or itself shared or organization-authenticated, or the mailbox is its own.",scope_missing:"The delegating account's grant lacks the shared mailbox scope (reconnect it with options.sharedMailboxesEnabled).",shared_mailbox_inaccessible:"The delegating account's user cannot open the mailbox (no access, no such mailbox, or the login is refused).",account_exists:"The mailbox is already an account authenticated another way."})}).input(AccountIdInput.extend(OpenSharedMailboxRequest.shape)).output(Account)};import*as z38 from"zod";import*as z37 from"zod";var SchedulingWindow=named("SchedulingWindow",z37.object({start:TimeOfDay,end:TimeOfDay}).refine((window)=>window.end>window.start,{message:"end must be after start",path:["end"],params:{reason:"invalid_working_hours"}})),DayWindows=z37.array(SchedulingWindow).max(MAX_SCHEDULING_DAY_WINDOWS),SchedulingHours=named("SchedulingHours",z37.object({mon:DayWindows.optional(),tue:DayWindows.optional(),wed:DayWindows.optional(),thu:DayWindows.optional(),fri:DayWindows.optional(),sat:DayWindows.optional(),sun:DayWindows.optional()})),DEFAULT_SCHEDULING_HOURS={mon:[{start:"09:00",end:"17:00"}],tue:[{start:"09:00",end:"17:00"}],wed:[{start:"09:00",end:"17:00"}],thu:[{start:"09:00",end:"17:00"}],fri:[{start:"09:00",end:"17:00"}]},SlotMinutes2=z37.number().int().min(MIN_SLOT_MINUTES).max(MAX_SLOT_MINUTES),BufferMinutes=z37.number().int().min(0).max(MAX_SCHEDULING_BUFFER_MINUTES),EventTemplate={title:z37.string().min(1).max(MAX_SCHEDULING_TITLE_LENGTH).default("{{invitee}}"),description:z37.string().max(MAX_SCHEDULING_DESCRIPTION_LENGTH).optional(),location:z37.string().max(MAX_EVENT_LOCATION_LENGTH).optional(),onlineMeeting:z37.boolean().default(!1),onlineMeetingService:MeetingService.optional()},SchedulingLinkFields=named("SchedulingLinkFields",z37.object({name:z37.string().min(1).max(MAX_NAME_LENGTH),accountIds:z37.array(Id).min(1).max(MAX_AVAILABILITY_ACCOUNTS).refine((ids)=>new Set(ids).size===ids.length,"accountIds lists an account twice"),mode:AvailabilityMode.default("all"),durationMinutes:SlotMinutes2.default(30),stepMinutes:SlotMinutes2.default(30),windowDays:z37.number().int().min(1).max(MAX_SCHEDULING_WINDOW_DAYS).default(14),timeZone:z37.string().min(1).max(MAX_TIME_ZONE_LENGTH),hours:SchedulingHours.default(DEFAULT_SCHEDULING_HOURS),bufferBeforeMinutes:BufferMinutes.default(0),bufferAfterMinutes:BufferMinutes.default(0),minimumNoticeMinutes:z37.number().int().min(0).max(MAX_SCHEDULING_NOTICE_MINUTES).default(0),maxPerDay:z37.number().int().min(1).max(MAX_SCHEDULING_PER_DAY).nullable().default(null),...EventTemplate})),SchedulingLink=named("SchedulingLink",SchedulingLinkFields.extend({id:Id,createdAt:Timestamp,updatedAt:Timestamp})),SchedulingLinkPage=Page(SchedulingLink),SchedulingLinkIdInput=named("SchedulingLinkIdInput",z37.object({linkId:Id})),CreateSchedulingLink=named("CreateSchedulingLink",SchedulingLinkFields),UpdateSchedulingLink=named("UpdateSchedulingLink",SchedulingLinkFields.partial().extend({description:z37.string().max(MAX_SCHEDULING_DESCRIPTION_LENGTH).nullable().optional(),location:z37.string().max(MAX_EVENT_LOCATION_LENGTH).nullable().optional(),onlineMeetingService:MeetingService.nullable().optional(),maxPerDay:z37.number().int().min(1).max(MAX_SCHEDULING_PER_DAY).nullable().optional()})),ListSchedulingLinksQuery=named("ListSchedulingLinksQuery",z37.object({limit:pageLimit(MAX_SCHEDULING_SLOTS,100),cursor:Cursor.optional()})),SchedulingSlotsQuery=named("SchedulingSlotsQuery",z37.object({from:QueryTimestamp.optional(),to:QueryTimestamp.optional(),limit:pageLimit(MAX_SCHEDULING_SLOTS,DEFAULT_SCHEDULING_SLOTS)})),SchedulingSlotsInput=withDateRange(SchedulingLinkIdInput.extend(SchedulingSlotsQuery.shape),{from:"from",to:"to",maxSpanMs:MAX_SCHEDULING_WINDOW_DAYS*DAY_MS}),SchedulingSlot=named("SchedulingSlot",z37.object({start:Timestamp,end:Timestamp,accountIds:z37.array(Id)})),SchedulingSlots=named("SchedulingSlots",z37.object({items:z37.array(SchedulingSlot)})),CreateBooking=named("CreateBooking",z37.object({start:QueryTimestamp,invitee:AddressInput,notes:z37.string().max(MAX_BOOKING_NOTES_LENGTH).optional()})),CreateBookingInput=SchedulingLinkIdInput.extend(CreateBooking.shape),BookingStatus=named("BookingStatus",z37.enum(["booked","cancelled"])),Booking=named("Booking",z37.object({id:Id,linkId:Id,accountId:Id,eventId:Id.nullable(),start:Timestamp,end:Timestamp,invitee:AddressInput,notes:z37.string().nullable(),status:BookingStatus,createdAt:Timestamp,cancelledAt:Timestamp.nullable()})),BookingIdInput=named("BookingIdInput",z37.object({bookingId:Id})),SCHEDULING_WEEKDAYS=Weekday.options;var LINK_NOT_FOUND=errors({scheduling_link_not_found:"The scheduling link does not exist."}),UNKNOWN_ACCOUNT=errors({unknown_account:"accountIds names an account the tenant does not have (data lists it)."}),LINK_INPUT=errors({invalid_time_zone:"timeZone is unknown.",invalid_working_hours:"A day's hours do not end after they start.",multiple_meeting_options:"Both onlineMeeting and onlineMeetingService are set."}),schedulingLinks={list:base.route({method:"GET",path:"/v1/scheduling-links",tags:["Scheduling"],summary:"List scheduling links",description:"Newest first."}).errors(INVALID_CURSOR).input(ListSchedulingLinksQuery).output(SchedulingLinkPage),get:base.route({method:"GET",path:"/v1/scheduling-links/{linkId}",tags:["Scheduling"],summary:"Get a scheduling link"}).errors(LINK_NOT_FOUND).input(SchedulingLinkIdInput).output(SchedulingLink),create:base.route({method:"POST",path:"/v1/scheduling-links",tags:["Scheduling"],summary:"Create a scheduling link",description:"accountIds are accounts of the tenant whose calendars the link offers. mode all books every account into one event; mode any books one of them, the one with the fewest bookings of this link in the last 30 days (any status), the earliest in accountIds when tied.",successStatus:201}).errors({...planModule("calendar"),...UNKNOWN_ACCOUNT,...LINK_INPUT}).input(CreateSchedulingLink).output(SchedulingLink),update:base.route({method:"PATCH",path:"/v1/scheduling-links/{linkId}",tags:["Scheduling"],summary:"Update a scheduling link",description:"Only the fields given change. Existing bookings keep their events."}).errors({...LINK_NOT_FOUND,...UNKNOWN_ACCOUNT,...LINK_INPUT}).input(SchedulingLinkIdInput.extend(UpdateSchedulingLink.shape)).output(SchedulingLink),delete:base.route({method:"DELETE",path:"/v1/scheduling-links/{linkId}",tags:["Scheduling"],summary:"Delete a scheduling link",description:"Bookings of the link are deleted with it. Their events are left as they are.",successStatus:204}).errors(LINK_NOT_FOUND).input(SchedulingLinkIdInput).output(z38.void()),slots:base.route({method:"GET",path:"/v1/scheduling-links/{linkId}/slots",tags:["Scheduling"],summary:"List bookable slots",description:"Slots inside the link's window (windowDays ahead of now, in its time zone), not before its minimum notice, inside its hours, clear of each account's own calendar once widened by the buffers, and under maxPerDay. from and to narrow that range. accountIds on a slot are the accounts free for it."}).errors({...LINK_NOT_FOUND,...planModule("calendar"),...errors({invalid_range:"to is not after from, or the range is longer than the link may look ahead."})}).input(SchedulingSlotsInput).output(SchedulingSlots)},bookings={create:base.route({method:"POST",path:"/v1/scheduling-links/{linkId}/bookings",tags:["Scheduling"],summary:"Book a slot",description:"Books start only when it is one of the link's slots at that moment. mode any hosts it on the account with the fewest bookings of this link in the last 30 days among those free then (the earliest in accountIds when tied); mode all creates the event on the first account and invites the others. The invitee is an attendee. The slot is claimed in the database before the event is created, so two bookings of the same account and start cannot both succeed (slot_taken). Sends booking.created.",successStatus:201}).errors({...LINK_NOT_FOUND,...planModule("calendar"),...CALENDAR_WRITE,...MEETING_SERVICE,...errors({slot_unavailable:"start is not a slot the link offers, or no account is free for it.",slot_taken:"The slot was booked meanwhile.",calendar_disabled:"The account the booking would land on has calendar sync off.",calendar_not_found:"That account has no calendar an event can be created in.",plan_module_unavailable:"The tenant's plan does not include calendar, or the link creates a Zoom or Webex meeting and the plan does not include meetings.",multiple_meeting_options:"The link sets both onlineMeeting and onlineMeetingService."})}).input(CreateBookingInput).output(Booking),cancel:base.route({method:"DELETE",path:"/v1/bookings/{bookingId}",tags:["Scheduling"],summary:"Cancel a booking",description:"Deletes the event (telling its attendees) and marks the booking cancelled. Sends booking.cancelled. An event already gone is still a successful cancel."}).errors({...planModule("calendar"),...CALENDAR_PROVIDER,...errors({booking_not_found:"The booking does not exist.",booking_cancelled:"The booking is already cancelled.",calendar_disabled:"The account's calendar sync is off, so the event cannot be cancelled."})}).input(BookingIdInput).output(Booking)};import*as z39 from"zod";var LIST="With listId, content is always rendered, `{{rcpt.unsubscribeUrl}}`, `{{rcpt.address}}` and `{{rcpt.name}}` are the recipient's, and the message carries List-Unsubscribe with that same URL and List-Unsubscribe-Post: List-Unsubscribe=One-Click (RFC 8058). Click tracking never rewrites a List-Unsubscribe URL, so the body's link stays as written. A recipient on that list or on the global list is not sent to, at queue time and again at delivery (send.failed with code suppressed). Without listId the global list is consulted only when the suppressionCheck setting (the account's, else the workspace's) is always: then a send with any to, cc or bcc address on it is refused with suppressed (data.addresses names them) and fails at delivery likewise. Whether the provider's DKIM signature covers the List-Unsubscribe headers is up to its signing.",UNDO="A message without a sendAt later than the account's undo window (undoSendSeconds) from now waits out the window; undoUntil says until when cancelling the entry (DELETE .../outbox/{outboxId}) undoes the send.",DELIVERY="Delivery is at-least-once, except outcome_unknown (below), and without order across retries: an account's entries are first tried in queue order, but one backing off after a failed attempt does not hold up the later ones. A retry first looks for a copy that already went out. When no SMTP reply comes after the final dot was handed to the operating system (the connection dropped or timed out, or the attempt ran out of time), the server may have accepted the message, so it is not sent again: send.failed carries code outcome_unknown and the client app decides whether to resend.",TRACKING="With tracking, the HTML part gets a 1x1 pixel (opens) and its http(s) links point at Workwae's redirect (clicks), reported as message.opened and message.clicked and counted on the outbox entry; text parts are left as they are. With clicks, tracking.honeypot (else the account's trackingHoneypot) also adds one hidden link only machines follow; it does nothing without clicks, so asking for it with clicks off is refused rather than ignored. Tracking is per message, not per recipient: every recipient gets the same MIME.",MAIL_MERGE=`Each of at most ${MAX_MAIL_MERGE_ENTRIES} entries becomes its own outbox entry and message, addressed to its to alone, with its own Message-ID, events, bounce matching and tracking; the answer has one result per entry, in order. Content is rendered per entry with its params (a template always; inline content only for entries that carry params, or with listId). An entry that cannot be queued fails alone, its result naming the reason (invalid_recipient, invalid_message_id, duplicate_message_id, template_render_failed, plan_limit_sends once the day's sends run out, suppressed when listId is given and the address is on that list or the global list, or the suppressionCheck setting is always and it is on the global list, internal_error), while the others go ahead; the call itself succeeds. The body is the send body without to, cc, bcc and render: an unknown field is refused.`,COMPOSE_ERRORS={...MAIL_ACCOUNT,...errors({from_not_allowed:"from is not one of the account's identities.",attachments_too_large:"The attachments exceed the provider's limit.",unknown_reply_target:"inReplyToMessageId names no message of the account."}),...accountState(),...PROVIDER_FAILURES},TEMPLATE_ERRORS=errors({unknown_template:"template names no template of the tenant.",template_account_mismatch:"The template is bound to another account.",template_and_inline_fields:"template comes with an inline subject, html, text, previewText or format.",invalid_template:"Inline content to render is not valid Handlebars or uses partials or decorators; data.field names the field.",params_too_large:`params exceed ${MAX_RENDER_PARAMS_BYTES} bytes as JSON.`,template_render_failed:`Rendering fails (a missing helper, more than ${MAX_RENDER_ITERATIONS} block iterations, a field rendered past its limit, Markdown longer than ${MAX_MARKDOWN_LENGTH} characters, rendered or not); data.field names the field.`}),RENDERED_ONCE="Content is rendered when the request is taken, so later template edits never change it.",TRACKING_ERRORS=errors({honeypot_needs_click_tracking:"tracking.honeypot is true and click tracking is off.",plan_module_unavailable:"Tracking is asked for and the tenant's plan does not include it (nor do the account's tracking defaults apply then)."}),LIST_UNSUBSCRIBE_UNAVAILABLE=errors({list_unsubscribe_unavailable:"listId is given and unsubscribe links are unavailable."}),PLAN_LIMIT_SENDS=errors({plan_limit_sends:"The tenant already queued as many sends today (UTC) as its plan allows.",plan_required:PLAN_REQUIRED_CONDITION,spend_cap_reached:spendCapCondition("a send goes beyond the sends the plan includes")}),DRAFT_NOT_FOUND=errors({draft_not_found:"The message does not exist or is no draft."}),drafts={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/drafts",tags:["Mail"],summary:"Create a draft",description:RENDERED_ONCE,successStatus:201}).errors({...COMPOSE_ERRORS,...TEMPLATE_ERRORS,...PROVIDER_CHANGE,...MAIL_CHANGE,...errors({draft_lost:"The provider took the draft but did not answer it back."})}).input(AccountIdInput.extend(ComposeWithTemplate.shape)).output(MessageSummary),update:base.route({method:"PUT",path:"/v1/accounts/{accountId}/drafts/{messageId}",tags:["Mail"],summary:"Replace a draft's content",description:RENDERED_ONCE}).errors({...COMPOSE_ERRORS,...TEMPLATE_ERRORS,...PROVIDER_CHANGE,...MAIL_CHANGE,...DRAFT_NOT_FOUND}).input(MessageIdInput.extend(ComposeWithTemplate.shape)).output(MessageSummary),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/drafts/{messageId}",tags:["Mail"],summary:"Delete a draft",successStatus:204}).errors({...MAIL_ACCOUNT,...MAIL_CHANGE,...DRAFT_NOT_FOUND,...accountState(),...PROVIDER_CHANGE}).input(MessageIdInput).output(z39.void()),send:base.route({method:"POST",path:"/v1/accounts/{accountId}/drafts/{messageId}/send",tags:["Mail"],summary:"Send a draft",description:`${UNDO} ${TRACKING} A Gmail or Microsoft 365 draft is sent by the provider as it is stored, so it is never tracked (the account's defaults do not apply); an IMAP draft is. A cancelled draft-send leaves the draft in place. With the suppressionCheck setting always, a draft with a to, cc or bcc address on the global list is refused with suppressed, and fails likewise at delivery.`,successStatus:201}).errors({...MAIL_ACCOUNT,...errors({mail_permission_missing:"The account's grant is not mailPermissions full: Gmail sends a stored draft only with its modify or compose scope, so drafts are sent with full access only. Send the content with POST .../outbox instead."}),...DRAFT_NOT_FOUND,...accountState(),...errors({not_a_draft:"The message stopped being a draft while the send was queued.",no_recipients:"The draft has no to, cc or bcc recipients (as last indexed).",suppressed:"The suppressionCheck setting is always and a to, cc or bcc address of the draft is on the global list (as last indexed). data.addresses names them.",tracking_unsupported:"Tracking (opens, clicks or honeypot) is asked for a Gmail or Microsoft 365 draft."}),...TRACKING_ERRORS,...PLAN_LIMIT_SENDS,...BILLING_PAST_DUE}).input(MessageIdInput.extend(SendOptions.shape)).output(QueuedSend)},SEND_INPUT=AccountIdInput.extend(SendMessageRequest.shape).strict().superRefine((send,ctx)=>{if(send.to.length+send.cc.length+send.bcc.length===0)ctx.addIssue({code:"custom",path:["to"],message:"The message has no recipients (to, cc or bcc)",params:{reason:"no_recipients"}});for(let name of Object.keys(send.headers??{})){let lower=name.toLowerCase();if(lower==="in-reply-to"&&send.inReplyToMessageId!==void 0)ctx.addIssue({code:"custom",path:["headers",name],message:"In-Reply-To cannot come with inReplyToMessageId, which writes it"});if(lower.startsWith("list-")&&send.listId!==void 0)ctx.addIssue({code:"custom",path:["headers",name],message:`${name} cannot come with listId, which writes the list headers`})}}),SEND_NOT_FOUND=errors({send_not_found:"The account has no such outbox entry."}),MAIL_MERGE_NOT_FOUND=errors({mail_merge_not_found:"The account has no such mail merge."}),MAIL_MERGE_KEPT="A merge is kept as long as any of its entries is in the outbox, so it goes with the last of them.",outbox={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/outbox",tags:["Mail"],summary:"Send a message",description:`Queues the message as an outbox entry; delivery is reported with send.sent, send.retrying and send.failed events carrying its id as outboxId, and a later bounce with message.bounced. ${DELIVERY} ${UNDO} ${TRACKING} ${LIST} For one message per recipient, see POST .../mail-merges. ${RENDERED_ONCE} An unknown field is refused.`,successStatus:201}).errors({...COMPOSE_ERRORS,...TEMPLATE_ERRORS,...MAIL_SEND,...errors({no_recipients:"to, cc and bcc are all empty.",list_send_recipients:"listId is given and the message has not exactly one to and no cc or bcc.",suppressed:"listId is given and the recipient is on that list or the global list, or the suppressionCheck setting is always and a to, cc or bcc address is on the global list. data.addresses names them.",duplicate_message_id:"messageId is already the Message-ID of another send of the account."}),...TRACKING_ERRORS,...LIST_UNSUBSCRIBE_UNAVAILABLE,...PLAN_LIMIT_SENDS,...BILLING_PAST_DUE}).input(SEND_INPUT).output(QueuedSend),list:base.route({method:"GET",path:"/v1/accounts/{accountId}/outbox",tags:["Mail"],summary:"List queued and recent sends, newest first",description:"status keeps the entries in that status."}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR}).input(AccountIdInput.extend(ListOutboxQuery.shape)).output(OutboxEntryPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/outbox/{outboxId}",tags:["Mail"],summary:"Get a send",description:"One entry with its tracking counts, for as long as the outbox keeps it: a sent message's outboxId names it."}).errors({...MAIL_ACCOUNT,...SEND_NOT_FOUND}).input(OutboxIdInput).output(OutboxEntry),cancel:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/outbox/{outboxId}",tags:["Mail"],summary:"Cancel a queued send",description:"Undoes a send while it waits for its sendAt.",successStatus:204}).errors({...MAIL_ACCOUNT,...SEND_NOT_FOUND,...errors({send_not_cancellable:"The entry is no longer queued: it is being sent, was sent, failed for good or was cancelled (the message names which)."})}).input(OutboxIdInput).output(z39.void()),reschedule:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/outbox/{outboxId}",tags:["Mail"],summary:"Reschedule a queued send",description:"Moves sendAt, earlier or later; a past sendAt sends as soon as possible. Announced as send.rescheduled."}).errors({...MAIL_ACCOUNT,...SEND_NOT_FOUND,...errors({send_not_reschedulable:"The entry is no longer queued (it is being sent, was sent, failed for good or was cancelled), or it is queued again after a failed attempt; the message names which."})}).input(OutboxIdInput.extend(RescheduleSendRequest.shape)).output(OutboxEntry),tracking:base.route({method:"GET",path:"/v1/accounts/{accountId}/outbox/{outboxId}/tracking",tags:["Mail"],summary:"List a send's opens and clicks",description:"Newest first. At most 1000 are kept per send (the counts on the entry keep going), each for the tracking retention period. Each event carries what its request showed: the method (GET or HEAD), the Referer and Origin hosts, the mail client (Gmail's image proxy, web or app; Outlook on the web, desktop or mobile; Apple Mail and its Mail Privacy Protection; Yahoo, AOL, Thunderbird, Proton, Fastmail, Zoho, iCloud, a self-hosted webmail; else unknown), the device, OS and browser families, Cloudflare's visitor location (country, region, city, timezone, latitude and longitude, behind a trusted proxy only), the seconds after the send and since the send's previous hit, its sequence number, whether it was conditional, a Range request or a prefetch, the sec-fetch tuple, and the request headers (lower-cased, values cut at 1024 characters, at most 64, never Cookie, Authorization or Proxy-Authorization, forwarding and cf-* headers only from the trusted proxy; the full Referer is only here). Full addresses and headers stay with the tenant for the retention period."}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR,...SEND_NOT_FOUND}).input(OutboxIdInput.extend(ListTrackingEventsQuery.shape)).output(TrackingEventPage)},mailMerges={create:base.route({method:"POST",path:"/v1/accounts/{accountId}/mail-merges",tags:["Mail"],summary:"Send one message per recipient (mail merge)",description:`${MAIL_MERGE} ${DELIVERY} ${UNDO} ${TRACKING} ${LIST} What holds for the whole call is checked once and refuses it. ${RENDERED_ONCE} Answers 200 with the merge's id (GET .../mail-merges/{mailMergeId}, POST .../cancel to cancel what is still queued) and a result per entry. ${MAIL_MERGE_KEPT}`}).errors({...COMPOSE_ERRORS,...TEMPLATE_ERRORS,...MAIL_SEND,...errors({attachments_too_large:`The attachments exceed the provider's limit, or the attachments times the entries exceed ${MAX_MAIL_MERGE_ATTACHMENT_BYTES} bytes.`}),...TRACKING_ERRORS,...LIST_UNSUBSCRIBE_UNAVAILABLE,...BILLING_PAST_DUE}).input(AccountIdInput.extend(MailMergeRequest.shape).strict()).output(MailMergeResultList),list:base.route({method:"GET",path:"/v1/accounts/{accountId}/mail-merges",tags:["Mail"],summary:"List mail merges, newest first",description:MAIL_MERGE_KEPT}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR}).input(AccountIdInput.extend(ListMailMergesQuery.shape)).output(MailMergePage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/mail-merges/{mailMergeId}",tags:["Mail"],summary:"Get a mail merge",description:`The merge with its entries counted by outbox status. ${MAIL_MERGE_KEPT}`}).errors({...MAIL_ACCOUNT,...MAIL_MERGE_NOT_FOUND}).input(MailMergeIdInput).output(MailMerge),cancel:base.route({method:"POST",path:"/v1/accounts/{accountId}/mail-merges/{mailMergeId}/cancel",tags:["Mail"],summary:"Cancel a mail merge's queued entries",description:"Cancels every entry of the merge that is still queued, as DELETE .../outbox/{outboxId} does for one (the payload is dropped, send.cancelled is announced for each). Entries being sent, sent, failed or cancelled are left alone; cancelled is how many this call cancelled, 0 when none was left."}).errors({...MAIL_ACCOUNT,...MAIL_MERGE_NOT_FOUND}).input(MailMergeIdInput).output(MailMergeCancelled)};var settings={get:base.route({method:"GET",path:"/v1/settings",tags:["Settings"],summary:"Get tenant settings"}).output(TenantSettings),update:base.route({method:"PATCH",path:"/v1/settings",tags:["Settings"],summary:"Update tenant settings",description:"bodyStorage applies to every account without its own override; changing it restarts their syncs (full stores and backfills every indexed message, none deletes stored content). mcpSendPolicy limits what agents send through the MCP server only (/v1 sends are not held to it); its keys present change, absent ones stay. Requires the admin scope."}).errors({...PLAN_LIMIT_ACCOUNTS,...bodyStorage("bodyStorage"),...errors({redirect_uri_not_allowed:"A redirect URI is not absolute https (http only for localhost)."})}).input(UpdateTenantSettings).output(TenantSettings)};import*as z40 from"zod";var base2=base.errors(planModule("mail")),LIST_NOT_FOUND=errors({suppression_list_not_found:"The list does not exist (the global list has none to change)."}),REVIEW_NOT_FOUND=errors({suppression_review_not_found:"The review does not exist."}),suppressions={list:base2.route({method:"GET",path:"/v1/suppressions",tags:["Suppressions"],summary:"List suppression lists",description:"The global list first, then every named list by listId, with its counts by source."}).output(SuppressionListList),create:base2.route({method:"POST",path:"/v1/suppressions",tags:["Suppressions"],summary:"Create a suppression list",description:"id is what sends name as listId; it cannot change later, the name and description can. A send or add naming a list that does not exist creates it too, named after its id.",successStatus:201}).errors(errors({suppression_list_exists:"A list with that id exists."})).input(CreateSuppressionList).output(SuppressionList),update:base2.route({method:"PATCH",path:"/v1/suppressions/{listId}",tags:["Suppressions"],summary:"Update a suppression list",description:"Only the fields given change; description null clears it. The global list cannot be changed."}).errors(LIST_NOT_FOUND).input(ListIdInput.extend(UpdateSuppressionList.shape)).output(SuppressionList),delete:base2.route({method:"DELETE",path:"/v1/suppressions/{listId}",tags:["Suppressions"],summary:"Delete a suppression list",description:"Deletes the list and its pending reviews and, with ?withEntries=true, every address on it: later sends naming it reach those addresses again (unless the global list holds them), and the next one naming it creates it anew. The global list cannot be deleted.",successStatus:204,inputStructure:"detailed"}).errors({...LIST_NOT_FOUND,...errors({suppression_list_not_empty:"The list holds addresses and withEntries is not true."})}).input(DeleteSuppressionListInput).output(z40.void()),entries:{list:base2.route({method:"GET",path:"/v1/suppressions/{listId}/entries",tags:["Suppressions"],summary:"List suppressed addresses",description:"Addresses on one list, by address; a list without addresses is empty, not missing. address keeps those containing it, source those that came from it."}).errors(INVALID_CURSOR).input(SuppressionListIdInput.extend(ListSuppressionEntriesQuery.shape)).output(SuppressionEntryPage),add:base2.route({method:"POST",path:"/v1/suppressions/{listId}/entries",tags:["Suppressions"],summary:"Suppress addresses",description:`Puts up to ${MAX_SUPPRESSION_BATCH} addresses on the list in one call; a repeated address counts once. added counts the addresses put on the list, alreadyListed those that were on it already, which are left as they were. A list that does not exist is created, named after its listId. Answers 200: entries are listed, not fetched one by one. Emits no suppression.created: that event reports what recipients and bounces did.`}).input(SuppressionListIdInput.extend(AddSuppressionEntriesRequest.shape)).output(SuppressionEntriesAdded),remove:base2.route({method:"DELETE",path:"/v1/suppressions/{listId}/entries/{address}",tags:["Suppressions"],summary:"Release an address",description:"Later sends naming the list reach the address again (unless another list they consult holds it).",successStatus:204}).errors(errors({suppression_entry_not_found:"The address is not on the list."})).input(SuppressionEntryInput).output(z40.void())}},suppressionReviews={list:base2.route({method:"GET",path:"/v1/suppression-reviews",tags:["Suppressions"],summary:"List opt-outs to review",description:"Detected opt-outs waiting for a person, newest first; listId keeps one list's. Holds AI-classified opt-out replies while optOutHandling is review, and auto-replies saying the person is no longer at the address (source inactive, on the global list) while it is review or suppress. A detection of an address already on its list, or already waiting, is not queued again."}).errors(INVALID_CURSOR).input(ListSuppressionReviewsQuery).output(SuppressionReviewPage),approve:base2.route({method:"POST",path:"/v1/suppression-reviews/{reviewId}/approve",tags:["Suppressions"],summary:"Approve an opt-out",description:"Puts the address on the review's list with the review's source and removes the review. added is false when the address was on the list already. Emits suppression.created when it was not."}).errors(REVIEW_NOT_FOUND).input(ReviewIdInput).output(SuppressionAdded),dismiss:base2.route({method:"POST",path:"/v1/suppression-reviews/{reviewId}/dismiss",tags:["Suppressions"],summary:"Dismiss an opt-out",description:"Removes the review without suppressing anything; a later detection of the same address queues it again.",successStatus:204}).errors(REVIEW_NOT_FOUND).input(ReviewIdInput).output(z40.void())};import*as z41 from"zod";var base3=base.errors(planModule("mail")),NOT_FOUND4=errors({template_not_found:"The template does not exist."}),SYNTAX=errors({invalid_template:"A field is not valid Handlebars or uses partials or decorators (data names the field, the message is the parser's)."}),NAME_TAKEN=errors({template_name_taken:"The scope (the tenant, or the bound account) already has a template of that name."}),UNKNOWN_ACCOUNT2=errors({unknown_account:"The accountId names an account the tenant does not have (data lists it)."}),templates={list:base3.route({method:"GET",path:"/v1/templates",tags:["Templates"],summary:"List templates",description:"Newest first, without their content (GET /v1/templates/{templateId} has it). accountId lists those bound to that account; usableBy those the account may send with (the tenant's shared ones and its own); name the ones of that exact name, one per scope; query those whose name contains it, ignoring case."}).errors(INVALID_CURSOR).input(ListTemplatesQuery).output(TemplateSummaryPage),get:base3.route({method:"GET",path:"/v1/templates/{templateId}",tags:["Templates"],summary:"Get a template"}).errors(NOT_FOUND4).input(TemplateIdInput).output(Template),create:base3.route({method:"POST",path:"/v1/templates",tags:["Templates"],summary:"Create a template",description:"Every field is checked to compile as Handlebars.",successStatus:201}).errors({...SYNTAX,...UNKNOWN_ACCOUNT2,...NAME_TAKEN}).input(CreateTemplate).output(Template),update:base3.route({method:"PATCH",path:"/v1/templates/{templateId}",tags:["Templates"],summary:"Update a template",description:"Only the fields given change; queued sends keep what they were rendered with."}).errors({...NOT_FOUND4,...SYNTAX,...NAME_TAKEN}).input(TemplateIdInput.extend(UpdateTemplate.shape)).output(Template),delete:base3.route({method:"DELETE",path:"/v1/templates/{templateId}",tags:["Templates"],summary:"Delete a template",description:"Queued sends keep their rendered content.",successStatus:204}).errors(NOT_FOUND4).input(TemplateIdInput).output(z41.void())},templateRenders={create:base3.route({method:"POST",path:"/v1/template-renders",tags:["Templates"],summary:"Render a template without sending",description:"Renders a stored template (templateId) or inline fields with params, as a send would. account.email and account.name come from accountId, else from the template's own account, else render empty."}).errors({...SYNTAX,...UNKNOWN_ACCOUNT2,...errors({template_and_inline_fields:"The templateId comes with inline fields or format.",unknown_template:"The templateId names no template of the tenant.",template_account_mismatch:"The template is bound to another account than accountId.",params_too_large:`The params exceed ${MAX_RENDER_PARAMS_BYTES} bytes as JSON.`,template_render_failed:`Rendering fails: a missing helper, more than ${MAX_RENDER_ITERATIONS} block iterations, a field rendered past its limit, or Markdown longer than ${MAX_MARKDOWN_LENGTH} characters (data names the field).`})}).input(RenderTemplateRequest).output(RenderedTemplate)};import*as z43 from"zod";import*as z42 from"zod";var ThreadSummary=named("ThreadSummary",z42.object({id:ProviderId,subject:z42.string().nullable(),snippet:z42.string().nullable(),participants:z42.array(Address),messageCount:z42.number().int(),unreadCount:z42.number().int(),isFlagged:z42.boolean(),hasAttachments:z42.boolean(),hasDrafts:z42.boolean(),mailboxIds:z42.array(Id),latestMessageId:Id,lastMessageAt:Timestamp.nullable()})),Thread=named("Thread",ThreadSummary.extend({messages:z42.array(MessageSummary)})),ThreadSummaryPage=Page(ThreadSummary),ThreadIdInput=named("ThreadIdInput",AccountIdInput.extend({threadId:ProviderId})),ListThreadsQuery=named("ListThreadsQuery",z42.object({mailboxId:Id.optional(),limit:pageLimit(MAX_THREAD_PAGE_SIZE,DEFAULT_THREAD_PAGE_SIZE),cursor:Cursor.optional()})),UpdateThread=named("UpdateThread",z42.object({isRead:z42.boolean().optional(),mailboxId:Id.optional()}));var THREAD_NOT_FOUND=errors({thread_not_found:"The account has no message in the thread."}),ACTION_ERRORS={...MAIL_ACCOUNT,...MAIL_CHANGE,...THREAD_NOT_FOUND,...accountState(),...PROVIDER_CHANGE},PART_WAY="A failure part way leaves the messages done so far changed; repeating the call finishes the rest.",threads={list:base.route({method:"GET",path:"/v1/accounts/{accountId}/threads",tags:["Mail"],summary:"List threads, latest activity first",description:"Covers the account's sync window. With mailboxId, lists threads with a message in that mailbox, ordered by their latest message there; each summary still describes the whole thread."}).errors({...MAIL_ACCOUNT,...INVALID_CURSOR,...UNKNOWN_MAILBOX}).input(AccountIdInput.extend(ListThreadsQuery.shape)).output(ThreadSummaryPage),get:base.route({method:"GET",path:"/v1/accounts/{accountId}/threads/{threadId}",tags:["Mail"],summary:"Get a thread with its messages, oldest first"}).errors({...MAIL_ACCOUNT,...THREAD_NOT_FOUND}).input(ThreadIdInput).output(Thread),update:base.route({method:"PATCH",path:"/v1/accounts/{accountId}/threads/{threadId}",tags:["Mail"],summary:"Mark a thread read or unread, or move it",description:`Applies the change to every message of the thread through the message operations; messages already in that state are skipped. mailboxId moves each message there (Gmail: replaces its folder-like labels). ${PART_WAY}`}).errors({...ACTION_ERRORS,...UNKNOWN_MAILBOX}).input(ThreadIdInput.extend(UpdateThread.shape)).output(Thread),delete:base.route({method:"DELETE",path:"/v1/accounts/{accountId}/threads/{threadId}",tags:["Mail"],summary:"Move a thread to Trash",description:`Moves every message of the thread that is not already there to Trash. ${PART_WAY}`,successStatus:204}).errors(ACTION_ERRORS).input(ThreadIdInput).output(z43.void())};import*as z45 from"zod";import*as z44 from"zod";var TrackingDomainHostname=named("TrackingDomainHostname",customDomainHostname("track.example.com")),TrackingDomainStatus=named("TrackingDomainStatus",z44.enum(CUSTOM_DOMAIN_STATUSES)),TrackingDomainRecord=named("TrackingDomainRecord",customDomainRecord()),TrackingDomain=named("TrackingDomain",z44.object({hostname:TrackingDomainHostname,status:TrackingDomainStatus,records:z44.array(TrackingDomainRecord),ownership:TrackingDomainRecord,error:z44.string().nullable(),activatedAt:Timestamp.nullable(),createdAt:Timestamp})),SetTrackingDomain=named("SetTrackingDomain",z44.object({hostname:TrackingDomainHostname}));var UNAVAILABLE2=errors({tracking_domain_unavailable:"The deployment has no Cloudflare for SaaS configured for tracking domains."}),NOT_FOUND5=errors({tracking_domain_not_found:"The workspace has no tracking domain."}),EDGE2=errors({tracking_domain_edge_unavailable:"The edge provider (Cloudflare) refused or could not be reached."}),trackingDomain={get:base.route({method:"GET",path:"/v1/settings/tracking-domain",tags:["Settings"],summary:"Get the workspace's tracking domain",description:"The workspace's own host for tracking pixels, click links and unsubscribe URLs, with the DNS records to publish and where setting it up stands. Requires the admin scope."}).errors({...UNAVAILABLE2,...NOT_FOUND5}).output(TrackingDomain),set:base.route({method:"PUT",path:"/v1/settings/tracking-domain",tags:["Settings"],summary:"Set the workspace's tracking domain",description:"Registers the hostname with the edge and answers the DNS records to publish: a CNAME to the deployment's tracking host (records), any TXT records the edge asks for (records), and the ownership TXT record that is random to this claim (ownership). The domain is pending until the ownership record is published and the edge reports the hostname and its certificate active (checked every few minutes, or on POST /v1/settings/tracking-domain/refresh); only then do new tracked sends link to it. Replaces the workspace's current tracking domain once the new hostname is registered, so a refusal leaves the current one serving; the replaced one stops serving then, and mail already sent keeps the host it was sent with. Needs the plan's tracking module. Requires the admin scope."}).errors({...UNAVAILABLE2,...EDGE2,...planModule("tracking"),...errors({tracking_domain_not_allowed:"The hostname is one of the deployment's own, or a name the edge cannot take.",tracking_domain_taken:"Another workspace uses the hostname, or this workspace has it as its connect domain."})}).input(SetTrackingDomain).output(TrackingDomain),delete:base.route({method:"DELETE",path:"/v1/settings/tracking-domain",tags:["Settings"],summary:"Remove the workspace's tracking domain",description:"Removes the hostname from the edge and the workspace. New tracked sends use the deployment's tracking host again; links already in sent mail on the removed domain stop working. Requires the admin scope.",successStatus:204}).errors({...UNAVAILABLE2,...NOT_FOUND5,...EDGE2}).output(z45.void()),refresh:base.route({method:"POST",path:"/v1/settings/tracking-domain/refresh",tags:["Settings"],summary:"Check the workspace's tracking domain now",description:"Reads the hostname's state from the edge now instead of waiting for the next check, and answers the updated domain. Requires the admin scope."}).errors({...UNAVAILABLE2,...NOT_FOUND5,...EDGE2}).output(TrackingDomain)};import*as z46 from"zod";var BillingPeriod=named("BillingPeriod",z46.object({start:Timestamp,end:Timestamp,firstDay:DateOnly})),EffectivePlan=named("EffectivePlan",z46.object({id:Id.nullable(),name:z46.string(),limits:PlanLimits})),UsageDay=named("UsageDay",z46.object({day:DateOnly,liveAccounts:z46.number().int(),mailAccounts:z46.number().int(),calendarContactsAccounts:z46.number().int(),meetingAccounts:z46.number().int(),apiCalls:z46.record(z46.string(),z46.number().int()),sends:z46.number().int(),webhookDeliveries:z46.number().int(),aiClassifications:z46.number().int(),aiDrafts:z46.number().int(),emailValidations:z46.number().int(),storedBytes:z46.number().int().nullable()})),ValidationCreditBalance=named("ValidationCreditBalance",z46.object({included:z46.number().int().min(0),includedExpiresAt:Timestamp.nullable(),purchased:z46.number().int().min(0)})),CurrentUsage=named("CurrentUsage",z46.object({accounts:z46.number().int(),webhookEndpoints:z46.number().int(),sendsToday:z46.number().int(),emailValidationsToday:z46.number().int(),validationCredits:ValidationCreditBalance.nullable(),storedBytes:z46.number().int()})),Usage=named("Usage",z46.object({plan:EffectivePlan,current:CurrentUsage,days:z46.array(UsageDay),period:BillingPeriod.nullable()})),DAY_MS2=86400000,UsageQuery=named("UsageQuery",withDateRange(z46.object({from:DateOnly.optional(),to:DateOnly.optional()}),{from:"from",to:"to",inclusive:!0,maxSpanMs:(MAX_USAGE_RANGE_DAYS-1)*DAY_MS2}).superRefine((q,ctx)=>{if(q.from===void 0||q.to!==void 0)return;let span=Date.parse(new Date().toISOString().slice(0,10))-Date.parse(q.from);if(span<0||span>(MAX_USAGE_RANGE_DAYS-1)*DAY_MS2)ctx.addIssue({code:"custom",path:["from"],message:span<0?"from must be on or before today when to is left out":`The range may span at most ${MAX_USAGE_RANGE_DAYS} days`,params:{reason:"invalid_range"}})}));var usage={get:base.route({method:"GET",path:"/v1/usage",tags:["Usage"],summary:"Read usage and plan limits",description:"Per UTC day: live accounts (sampled), API calls by scope, sends, webhook deliveries, AI classifications and drafts, and stored bytes (listed once a day). from and to default to the last 30 days; today's counters are live."}).errors(errors({invalid_range:`from is after to (or after today, with to left out), or the range spans more than ${MAX_USAGE_RANGE_DAYS} days.`})).input(UsageQuery).output(Usage)};import*as z48 from"zod";import*as z47 from"zod";var ADDRESS_PATTERN=/^(?:[^\s@]+)?@[^\s@.]+(?:\.[^\s@.]+)+$/,WEBHOOK_HEADERS={id:"webhook-id",timestamp:"webhook-timestamp",signature:"webhook-signature"},WEBHOOK_SECRET_PREFIX="whsec_",AddressPattern=z47.string().trim().toLowerCase().max(MAX_EMAIL_LENGTH).regex(ADDRESS_PATTERN,"Expected an address or @domain"),WebhookMessageFilters=named("WebhookMessageFilters",z47.object({mailboxIds:z47.array(Id),mailboxRoles:z47.array(MailboxRole),from:z47.array(z47.string()),to:z47.array(z47.string()),subjectContains:z47.array(z47.string()),hasAttachments:z47.boolean().nullable(),direction:MessageDirection.nullable()})),WebhookFilters=named("WebhookFilters",z47.object({eventTypes:z47.array(EventType),accountIds:z47.array(Id),message:WebhookMessageFilters})),WebhookMessageFiltersInput=named("WebhookMessageFiltersInput",z47.object({mailboxIds:z47.array(Id).max(MAX_WEBHOOK_FILTER_VALUES).optional(),mailboxRoles:z47.array(MailboxRole).max(MailboxRole.options.length).optional(),from:z47.array(AddressPattern).max(MAX_WEBHOOK_FILTER_VALUES).optional(),to:z47.array(AddressPattern).max(MAX_WEBHOOK_FILTER_VALUES).optional(),subjectContains:z47.array(z47.string().trim().min(1).max(MAX_SUBJECT_FILTER_LENGTH)).max(MAX_WEBHOOK_FILTER_VALUES).optional(),hasAttachments:z47.boolean().nullable().optional(),direction:MessageDirection.nullable().optional()})),WebhookFiltersInput=named("WebhookFiltersInput",z47.object({eventTypes:z47.array(EventType).max(MAX_WEBHOOK_FILTER_VALUES).optional(),accountIds:z47.array(Id).max(MAX_WEBHOOK_FILTER_VALUES).optional(),message:WebhookMessageFiltersInput.optional()})),WebhookEndpoint=named("WebhookEndpoint",z47.object({id:Id,url:z47.url(),description:z47.string().nullable(),filters:WebhookFilters,isEnabled:z47.boolean(),disabledReason:z47.string().nullable(),lastSuccessAt:Timestamp.nullable(),failingSince:Timestamp.nullable(),previousSecretExpiresAt:Timestamp.nullable(),createdAt:Timestamp})),WebhookEndpointList=List(WebhookEndpoint),WebhookEndpointWithSecret=named("WebhookEndpointWithSecret",z47.object({endpoint:WebhookEndpoint,secret:z47.string()})),WebhookSecret=named("WebhookSecret",z47.object({secret:z47.string(),previousSecretExpiresAt:Timestamp.nullable()})),RotateWebhookSecretRequest=named("RotateWebhookSecretRequest",z47.object({graceSeconds:z47.number().int().min(0).max(MAX_WEBHOOK_SECRET_GRACE_SECONDS).default(DEFAULT_WEBHOOK_SECRET_GRACE_SECONDS)})),WebhookEndpointIdInput=named("WebhookEndpointIdInput",z47.object({endpointId:Id})),CreateWebhookEndpoint=named("CreateWebhookEndpoint",z47.object({url:z47.url(),description:z47.string().max(MAX_NOTE_LENGTH).optional(),filters:WebhookFiltersInput.optional()})),UpdateWebhookEndpoint=named("UpdateWebhookEndpoint",z47.object({url:z47.url().optional(),description:z47.string().max(MAX_NOTE_LENGTH).nullable().optional(),filters:WebhookFiltersInput.optional(),isEnabled:z47.boolean().optional()})),SendTestEventRequest=named("SendTestEventRequest",z47.object({eventType:EventType.default(TEST_EVENT_TYPE),accountId:Id.optional(),data:z47.looseObject({}).optional()}).superRefine((input,ctx)=>{if(input.data===void 0)return;let parsed=EVENTS[input.eventType].data.safeParse(input.data);if(parsed.success)return;for(let issue of parsed.error.issues)ctx.addIssue({code:"custom",message:issue.message,path:["data",...issue.path]})})),TestEventSent=named("TestEventSent",z47.object({eventId:EventId})),WebhookAttemptResult=named("WebhookAttemptResult",z47.enum(["succeeded","failed"])),WebhookAttempt=named("WebhookAttempt",z47.object({id:Id,eventId:EventId,eventType:EventType,accountId:Id.nullable(),attempt:z47.number().int(),statusCode:z47.number().int().nullable(),error:z47.string().nullable(),responseExcerpt:z47.string().nullable(),durationMs:z47.number().int(),isReplay:z47.boolean(),isTest:z47.boolean(),occurredAt:Timestamp})),WebhookAttemptPage=Page(WebhookAttempt),ListWebhookAttemptsQuery=named("ListWebhookAttemptsQuery",z47.object({limit:pageLimit(MAX_WEBHOOK_ATTEMPT_PAGE_SIZE,DEFAULT_WEBHOOK_ATTEMPT_PAGE_SIZE),cursor:Cursor.optional(),eventType:EventType.optional(),eventId:EventId.optional(),accountId:Id.optional(),result:WebhookAttemptResult.optional(),since:QueryTimestamp.optional(),until:QueryTimestamp.optional()})),ListWebhookAttemptsInput=withDateRange(WebhookEndpointIdInput.extend(ListWebhookAttemptsQuery.shape),{from:"since",to:"until"}),ReplayEventInput=named("ReplayEventInput",WebhookEndpointIdInput.extend({eventId:EventId})),ReplayRangeRequest=named("ReplayRangeRequest",withDateRange(z47.object({since:QueryTimestamp,until:QueryTimestamp}),{from:"since",to:"until"})),ReplayResult=named("ReplayResult",z47.object({queued:z47.number().int(),alreadyQueued:z47.number().int()})),FailedDeliveriesQuery=named("FailedDeliveriesQuery",withDateRange(z47.object({since:QueryTimestamp.optional(),until:QueryTimestamp.optional()}),{from:"since",to:"until"})),FailedDeliveryCount=named("FailedDeliveryCount",z47.object({count:z47.number().int()}));var ENDPOINT_NOT_FOUND=errors({webhook_endpoint_not_found:"The endpoint does not exist."}),ENDPOINT_DISABLED=errors({endpoint_disabled:"The endpoint is disabled."}),HTTPS_REQUIRED=errors({https_required:"url is not https."}),WEBHOOK_FILTER_ERRORS=errors({unknown_account:"filters.accountIds names an account the tenant does not have; data.ids lists them.",unknown_mailbox:"filters.message.mailboxIds names a mailbox the tenant does not have; data.ids lists them.",mailbox_outside_accounts:"filters.message.mailboxIds names a mailbox of an account that filters.accountIds leaves out."}),webhooks={list:base.route({method:"GET",path:"/v1/webhooks",tags:["Webhooks"],summary:"List webhook endpoints"}).output(WebhookEndpointList),create:base.route({method:"POST",path:"/v1/webhooks",tags:["Webhooks"],summary:"Create a webhook endpoint",description:"The signing secret is returned once.",successStatus:201}).errors({...HTTPS_REQUIRED,...WEBHOOK_FILTER_ERRORS,...errors({plan_limit_webhooks:"The tenant already has as many endpoints as its plan allows."})}).input(CreateWebhookEndpoint).output(WebhookEndpointWithSecret),update:base.route({method:"PATCH",path:"/v1/webhooks/{endpointId}",tags:["Webhooks"],summary:"Update a webhook endpoint",description:"The mailbox check runs whenever filters.message.mailboxIds or filters.accountIds changes."}).errors({...ENDPOINT_NOT_FOUND,...HTTPS_REQUIRED,...WEBHOOK_FILTER_ERRORS}).input(WebhookEndpointIdInput.extend(UpdateWebhookEndpoint.shape)).output(WebhookEndpoint),delete:base.route({method:"DELETE",path:"/v1/webhooks/{endpointId}",tags:["Webhooks"],summary:"Delete a webhook endpoint",successStatus:204}).errors(ENDPOINT_NOT_FOUND).input(WebhookEndpointIdInput).output(z48.void()),rotateSecret:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/rotate-secret",tags:["Webhooks"],summary:"Rotate the signing secret",description:`Deliveries are signed with the new secret at once. For graceSeconds (default ${DEFAULT_WEBHOOK_SECRET_GRACE_SECONDS}, at most ${MAX_WEBHOOK_SECRET_GRACE_SECONDS}) they also carry a signature by the old one, in the same webhook-signature header, so receivers can switch secrets without refusing a delivery; 0 stops the old secret at once. A rotation during another's grace ends that grace: only the secret it replaces is kept.`}).errors(ENDPOINT_NOT_FOUND).input(RotateWebhookSecretRequest.safeExtend(WebhookEndpointIdInput.shape)).output(WebhookSecret),test:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/test",tags:["Webhooks"],summary:"Send a test event",description:`Queues one event of eventType for delivery to this endpoint only, whatever its filters, signed like any other, with "isTest": true in its body; its attempts show in the delivery log. It carries the sample data for its type unless data is given, which must match the type's data. Test events are never in the change feed.`,successStatus:202}).errors({...ENDPOINT_NOT_FOUND,...ENDPOINT_DISABLED,...errors({unknown_account:"accountId names an account the tenant does not have; data.ids lists it."})}).input(SendTestEventRequest.safeExtend(WebhookEndpointIdInput.shape)).output(TestEventSent),attempts:base.route({method:"GET",path:"/v1/webhooks/{endpointId}/attempts",tags:["Webhooks"],summary:"Read the delivery log",description:"Delivery attempts to this endpoint, newest first: of the last 500 within 7 days, those matching the filters. A cursor stays valid while its attempt is in the log."}).errors({...ENDPOINT_NOT_FOUND,...INVALID_CURSOR,...errors({invalid_range:"until is not after since."})}).input(ListWebhookAttemptsInput).output(WebhookAttemptPage),replay:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/replay",tags:["Webhooks"],summary:"Replay a time range",description:"Queues every event in [since, until) that the endpoint would receive with its current filters again, with the same id and body as the first delivery and a fresh signature. Events the endpoint already has queued are not queued twice. Events past the tenant's eventRetentionDays cannot be replayed.",successStatus:202}).errors({...ENDPOINT_NOT_FOUND,...ENDPOINT_DISABLED,...errors({invalid_range:"until is not after since.",replay_range_too_large:`The range holds more than ${MAX_REPLAY_EVENTS} events.`})}).input(ReplayRangeRequest.safeExtend(WebhookEndpointIdInput.shape)).output(ReplayResult),replayEvent:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/events/{eventId}/replay",tags:["Webhooks"],summary:"Replay one event",description:"Queues the event again for this endpoint, whatever its filters, with the same id and body as the first delivery and a fresh signature; not twice if the endpoint already has it queued. Events past the tenant's eventRetentionDays cannot be replayed.",successStatus:202}).errors({...ENDPOINT_NOT_FOUND,...ENDPOINT_DISABLED,...errors({event_not_found:"The event is unknown or no longer retained."})}).input(ReplayEventInput).output(ReplayResult),countFailed:base.route({method:"GET",path:"/v1/webhooks/{endpointId}/failed-deliveries/count",tags:["Webhooks"],summary:"Count failed deliveries",description:"How many events replay-failed would queue now: those the endpoint gave up on (retries exhausted, or given up when it was disabled) that are still retained and not queued again, optionally only those created in [since, until)."}).errors({...ENDPOINT_NOT_FOUND,...errors({invalid_range:"until is not after since."})}).input(FailedDeliveriesQuery.safeExtend(WebhookEndpointIdInput.shape)).output(FailedDeliveryCount),replayFailed:base.route({method:"POST",path:"/v1/webhooks/{endpointId}/replay-failed",tags:["Webhooks"],summary:"Replay failed deliveries",description:"Queues every event the endpoint gave up on again, once each, optionally only those created in [since, until), with the same id and body as the first delivery and a fresh signature. The failed deliveries are replaced by the replays (their attempts stay in the delivery log), so calling again does not send them twice. Events past the tenant's eventRetentionDays cannot be replayed.",successStatus:202}).errors({...ENDPOINT_NOT_FOUND,...ENDPOINT_DISABLED,...errors({invalid_range:"until is not after since.",replay_range_too_large:`More than ${MAX_REPLAY_EVENTS} events failed; replay a narrower range.`})}).input(FailedDeliveriesQuery.safeExtend(WebhookEndpointIdInput.shape)).output(ReplayResult)};var v1Contract={accounts,mcpKeys,mailboxes,messages,search,threads,drafts,outbox,mailMerges,templates,templateRenders,suppressions,suppressionReviews,identities,mailSettings,calendars,calendarEvents,availability,schedulingLinks,bookings,meetingConnections,meetingConnectSessions,meetingApps,contacts,contactFolders,webhooks,changes,settings,trackingDomain,connectDomain,oauthApps,connectSessions,oauthAuthorizations,orgConnections,sharedMailboxes,imapAccounts,imapSettings,autoconfig,hostedConnectSessions,hostedMeetingConnectSessions,exports,usage,ai,emailValidations,auditLog};var WEBHOOK_TOLERANCE_SECONDS=300,WEBHOOK_VERIFICATION_MESSAGES={missing_headers:"The webhook headers are missing",timestamp:"The webhook timestamp is outside the tolerance",signature:"No webhook signature matches the secret",payload:"The webhook body is not a Workwae event"};class WebhookVerificationError extends Error{failure;constructor(failure,message=WEBHOOK_VERIFICATION_MESSAGES[failure]){super(message);this.failure=failure;this.name="WebhookVerificationError"}}var encoder=new TextEncoder;function header(headers,name){if(headers instanceof Headers)return headers.get(name);for(let[key,value]of Object.entries(headers))if(key.toLowerCase()===name)return Array.isArray(value)?value.join(" "):value??null;return null}function fromBase64(text){try{return Uint8Array.from(atob(text),(c)=>c.charCodeAt(0))}catch{return null}}async function mac(secret,id,timestamp,body){let keyBytes=fromBase64(secret.startsWith(WEBHOOK_SECRET_PREFIX)?secret.slice(WEBHOOK_SECRET_PREFIX.length):secret);if(!keyBytes||keyBytes.length===0)throw TypeError("The webhook secret is not base64");let key=await crypto.subtle.importKey("raw",keyBytes,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),prefix=encoder.encode(`${id}.${timestamp}.`),payload=typeof body==="string"?encoder.encode(body):new Uint8Array(body),signed=new Uint8Array(prefix.length+payload.length);return signed.set(prefix),signed.set(payload,prefix.length),new Uint8Array(await crypto.subtle.sign("HMAC",key,signed))}function equalInConstantTime(a,b){let difference=0;for(let i=0;i<a.length;i++)difference|=(a[i]??0)^(b[i]??0);return difference===0}async function checkWebhookSignature(secret,headers,body,options={}){let id=header(headers,WEBHOOK_HEADERS.id),timestamp=header(headers,WEBHOOK_HEADERS.timestamp),signatures=header(headers,WEBHOOK_HEADERS.signature);if(!id||!timestamp||!signatures)return"missing_headers";if(!/^[0-9]{1,15}$/.test(timestamp))return"timestamp";let nowSeconds=(options.now??new Date).getTime()/1000,tolerance=options.toleranceSeconds??WEBHOOK_TOLERANCE_SECONDS;if(Math.abs(nowSeconds-Number(timestamp))>tolerance)return"timestamp";let expected=await mac(secret,id,timestamp,body);for(let candidate of signatures.split(" ",MAX_SIGNATURE_CANDIDATES)){let[version,value]=candidate.split(",",2);if(version!=="v1"||!value)continue;let given=fromBase64(value);if(given?.length===expected.length&&equalInConstantTime(given,expected))return null}return"signature"}async function verifyWebhook(secret,headers,body,options={}){return await checkWebhookSignature(secret,headers,body,options)===null}import{createORPCClient,ORPCError}from"@orpc/client";import{ResponseValidationPlugin}from"@orpc/contract/plugins";import{OpenAPILink}from"@orpc/openapi-client/fetch";var DEFAULT_BASE_URL="https://api.workwae.com";function parseRetryAfter(value){if(value===null)return null;let trimmed=value.trim();if(/^\d+(\.\d+)?$/.test(trimmed))return Math.round(Number(trimmed)*1000);let at=Date.parse(trimmed);return Number.isNaN(at)?null:Math.max(0,at-Date.now())}function sleep(ms,signal){return new Promise((resolve,reject)=>{if(signal?.aborted){reject(signal.reason);return}let onAbort=()=>{clearTimeout(timer),reject(signal?.reason)},timer=setTimeout(()=>{signal?.removeEventListener("abort",onAbort),resolve()},ms);signal?.addEventListener("abort",onAbort,{once:!0})})}var BACKOFF_START_MS=500,SEEN=Symbol("workwae.seen");function retrying(send,options={}){let maxRetries=options.maxRetries??2,maxDelayMs=options.maxDelayMs??60000,wait=options.wait??sleep;return async(request,init,{signal,context})=>{if(request.method==="POST"&&!request.headers.has("idempotency-key"))request.headers.set("idempotency-key",crypto.randomUUID());for(let attempt=0;;attempt++){let last=attempt>=maxRetries,response=await send(last?request:request.clone(),init),seen=context[SEEN];if(seen)seen.requestId=response.headers.get("x-request-id")??void 0;if(last||response.status!==429&&response.status!==503)return response;let retryAfterMs=parseRetryAfter(response.headers.get("retry-after"));if(retryAfterMs!==null&&retryAfterMs>maxDelayMs)return response;await response.body?.cancel();let backoff=BACKOFF_START_MS*2**attempt*(0.5+Math.random()/2);await wait(retryAfterMs??Math.min(backoff,maxDelayMs),signal),signal?.throwIfAborted()}}}function createClient(options){let{apiKey}=options,link=new OpenAPILink(v1Contract,{url:options.baseUrl??DEFAULT_BASE_URL,headers:async({context})=>{let key=typeof apiKey==="function"?await apiKey():apiKey;return{...options.headers,...key&&{authorization:`Bearer ${key}`},...context.idempotencyKey&&{"idempotency-key":context.idempotencyKey}}},fetch:retrying(options.fetch??((request,init)=>fetch(request,init)),options.retry),interceptors:[async({next,...call})=>{let seen={requestId:void 0};try{return await next({...call,context:{...call.context,[SEEN]:seen}})}catch(error){if(error instanceof ORPCError)error.requestId=seen.requestId;throw error}}],clientInterceptors:[async({next})=>{let response=await next();if(response.status>=400){let read=response.body;response.body=()=>read().catch(()=>{return})}return response}],plugins:[new ResponseValidationPlugin(v1Contract)]});return createORPCClient(link)}import{ORPCError as ORPCError2}from"@orpc/client";import{isDefinedError,ORPCError as ORPCError3,safe}from"@orpc/client";function isApiError(error,reason){return error instanceof ORPCError2&&isErrorReason(error.code)&&(reason===void 0||error.code===reason)}function pages(list,input,options){return followCursor((cursor)=>list({...input,cursor},options),input.cursor,(page)=>page.nextCursor??void 0)}async function*paginate(list,input,options){for await(let page of pages(list,input,options))yield*page.items}class ChangeFeed{list;input;options;#cursor;constructor(list,input,options){this.list=list;this.input=input;this.options=options;this.#cursor=input.cursor}get cursor(){return this.#cursor}async*[Symbol.asyncIterator](){let feed=followCursor((cursor)=>this.list({...this.input,cursor},this.options),this.#cursor,(page)=>page.items.length===0?void 0:page.nextCursor);for await(let page of feed)yield*page.items,this.#cursor=page.nextCursor}}function changes2(list,input,options){return new ChangeFeed(list,input,options)}import*as z49 from"zod";var LaterEvent=z49.looseObject({id:EventId,type:z49.string(),accountId:Id.nullable(),createdAt:Timestamp,data:z49.looseObject({}),isTest:z49.literal(!0).exactOptional()});async function parseWebhook(secret,headers,body,options={}){let failure=await checkWebhookSignature(secret,headers,body,options);if(failure)throw new WebhookVerificationError(failure);let json;try{json=JSON.parse(typeof body==="string"?body:new TextDecoder().decode(body))}catch{throw new WebhookVerificationError("payload","The webhook body is not JSON")}let type=typeof json==="object"&&json!==null&&"type"in json?json.type:void 0;if(EventType.safeParse(type).success){let parsed=Event.safeParse(json);if(parsed.success)return{...json,...parsed.data,known:!0}}else{let later=LaterEvent.safeParse(json);if(later.success)return{...later.data,known:!1}}throw new WebhookVerificationError("payload")}export{ChangeFeed,EVENT_TYPES,MAX_SIGNATURE_CANDIDATES,MAX_TEMPLATE_BODY_LENGTH,MAX_TEMPLATE_DESCRIPTION_LENGTH,MAX_TEMPLATE_NAME_LENGTH,MAX_TEMPLATE_PREVIEW_TEXT_LENGTH,MAX_TEMPLATE_SUBJECT_LENGTH,ORPCError3 as ORPCError,WEBHOOK_TOLERANCE_SECONDS,WebhookVerificationError,changes2 as changes,checkWebhookSignature,createClient,isApiError,isDefinedError,pages,paginate,parseWebhook,safe,verifyWebhook};