import {z} from 'zod' export type UserName = string; export type MessageName = string; export type NotifyMethods = {email?: MessageName | undefined, sms?: MessageName | undefined, 'email-link-only'?: boolean | undefined}; export type NotifyKind = 'standard' | 'confirmation'; export type NotifyDetails = {users: Array, methods: NotifyMethods, kind?: NotifyKind | undefined}; export type NotifyAction = {notify: NotifyDetails}; export type DocumentName = string; export type AllowViewingDetails = {users: Array, documents: Array}; export type AllowViewingAction = {'allow-viewing': AllowViewingDetails}; export type DenyViewingDetails = {users: Array, documents: Array}; export type DenyViewingAction = {'deny-viewing': DenyViewingDetails}; export type CallbackName = string; export type CallbackDetails = {'callback-name': CallbackName}; export type CallbackAction = {callback: CallbackDetails}; export type SystemAction = NotifyAction | AllowViewingAction | DenyViewingAction | CallbackAction; export type ApprovedBy = {documents: Array, users: Array}; export type ApprovedByGroupOf = {'required-approvals': number, documents: Array, users: Array}; export type SignedBy = {documents: Array, users: Array}; export type SignedByGroupOf = {'required-signatures': number, documents: Array, users: Array}; export type ViewedBy = {documents: Array, users: Array}; export type Url = string; export type RedirectTo = {users: Array, url: Url}; export type Expect = {'approved-by'?: ApprovedBy | undefined, 'approved-by-group-of'?: ApprovedByGroupOf | undefined, 'signed-by'?: SignedBy | undefined, 'signed-by-group-of'?: SignedByGroupOf | undefined, 'viewed-by'?: ViewedBy | undefined, 'redirect-to'?: RedirectTo | undefined}; export type FlowDSLStageRegular = {[key: string]: {actions: Array, expect: Expect}}; export type ConfirmationAction = {notify: {users: Array, methods: NotifyMethods, kind: 'confirmation'}}; export type FlowDSLStageConfirmation = {[key: string]: {actions: Array, expect: Record}}; export type FlowDSLStage = FlowDSLStageRegular | FlowDSLStageConfirmation; export type DslVersion = string; export type AllowRejections = boolean; export type TimeOfDay = unknown; export type ReminderRelativeTime = {time_of_day: TimeOfDay, time_zone: string}; export type ReminderRepeat = 'never' | 'periodically'; export type ReminderDate = {type: 'absolute', date_time: unknown} | {type: 'relative', days_delay: number, time?: ReminderRelativeTime | undefined, repeat?: ReminderRepeat | undefined}; export type AutomaticReminderMethod = 'email' | 'sms' | 'email-and-sms'; export type AutomaticReminder = {'user-name': UserName, date: ReminderDate, method: AutomaticReminderMethod}; export type FlowDSL = {stages: Array, 'dsl-version': DslVersion, 'allow-rejections'?: AllowRejections | undefined, 'automatic-reminders'?: Array | undefined}; export type DocumentID = string; export type UserIdType = 'email' | 'phone_number' | 'user_id' | 'full_name'; export type OnfidoMethod = 'document' | 'document_and_photo' | 'document_and_video' | 'document_and_motion'; export type OnfidoCountry = string; export type OnfidoDocumentType = 'national_identity_card' | 'driving_licence' | 'passport' | 'residence_permit'; export type OnfidoDocumentCapture = 'mobile_only' | 'mobile_or_desktop'; export type OnfidoAdditionalReportInfo = 'driving_licence_info' | 'address_info'; export type NOUiLocale = 'nb' | 'en'; export type UiLocale = 'nb-NO' | 'en-GB' | 'da-DK' | 'sv-SE' | 'fi-FI' | 'sv-FI'; export type TupasBank = 'nordea' | 'opbank' | 'danske' | 'handelsbanken' | 'aland' | 'sbank' | 'aktia' | 'popbank' | 'savingsbank' | 'omasp' | 'mobileid'; export type AuthenticationConfiguration = {provider: 'sms_otp', max_failures: number, attempts_per_sms?: number | undefined} | {provider: 'onfido', max_failures: number, method?: OnfidoMethod | undefined, allowed_document_countries?: Array | undefined, allowed_document_types?: Array | undefined, document_capture?: OnfidoDocumentCapture | undefined, additional_report_info?: OnfidoAdditionalReportInfo | undefined, email?: string | undefined} | {provider: 'se_bankid', max_failures: number} | {provider: 'dk_mitid', max_failures: number} | {provider: 'no_bankid', max_failures: number, ui_locale?: NOUiLocale | undefined} | {provider: 'ftn', max_failures: number, ui_locale?: UiLocale | undefined, force_bank?: TupasBank | undefined}; export type UserConfiguration = {id: string, id_type: UserIdType, auth_to_view?: AuthenticationConfiguration | undefined, auth_to_view_archived?: AuthenticationConfiguration | undefined}; export type Message = string; export type ProcessParameters = {documents: {[key: string]: DocumentID}, users: {[key: string]: UserConfiguration}, messages: {[key: string]: Message}}; export type CallbackUrl = string; export type AuthMethod = {type: 'basic', user: string, password: string} | {type: 'none'}; export type CallbackVersion = 'v1'; export type Callback = {url: CallbackUrl, auth?: AuthMethod | undefined, version: CallbackVersion}; export type FolderID = string; export type CreateDraft = {title?: string | undefined, process?: FlowDSL | undefined, process_parameters?: ProcessParameters | undefined, callback?: Callback | undefined, folder_id?: FolderID | undefined, duration?: ({days?: number | undefined, hours?: number | undefined, minutes?: number | undefined}) | undefined}; export type CreateApiExperimentalFlowDraftsArgs = {body: CreateDraft}; export type CreateApiExperimentalFlowDraftsOptions = {baseUrl?: string | undefined, OAuth10?: string | undefined, fetch?: typeof fetch | undefined}; export const userName = z.string(); export const messageName = z.string(); export const notifyMethods = z.object({email: messageName.optional(), sms: messageName.optional(), "email-link-only": z.boolean().optional()}); export const notifyKind = z.enum(["standard", "confirmation"]); export const notifyDetails = z.object({users: z.array(userName), methods: notifyMethods, kind: notifyKind.optional()}); export const notifyAction = z.object({notify: notifyDetails}); export const documentName = z.string(); export const allowViewingDetails = z.object({users: z.array(userName), documents: z.array(documentName)}); export const allowViewingAction = z.object({"allow-viewing": allowViewingDetails}); export const denyViewingDetails = z.object({users: z.array(userName), documents: z.array(documentName)}); export const denyViewingAction = z.object({"deny-viewing": denyViewingDetails}); export const callbackName = z.string().max(128); export const callbackDetails = z.object({"callback-name": callbackName}); export const callbackAction = z.object({callback: callbackDetails}); export const systemAction = z.union([notifyAction, allowViewingAction, denyViewingAction, callbackAction]); export const approvedBy = z.object({documents: z.array(documentName), users: z.array(userName)}); export const approvedByGroupOf = z.object({"required-approvals": z.number().int().gte(0).lte(18446744073709552000), documents: z.array(documentName), users: z.array(userName)}); export const signedBy = z.object({documents: z.array(documentName), users: z.array(userName)}); export const signedByGroupOf = z.object({"required-signatures": z.number().int().gte(0).lte(18446744073709552000), documents: z.array(documentName), users: z.array(userName)}); export const viewedBy = z.object({documents: z.array(documentName), users: z.array(userName)}); export const url = z.string(); export const redirectTo = z.object({users: z.array(userName), url: url}); export const expect = z.object({"approved-by": approvedBy.optional(), "approved-by-group-of": approvedByGroupOf.optional(), "signed-by": signedBy.optional(), "signed-by-group-of": signedByGroupOf.optional(), "viewed-by": viewedBy.optional(), "redirect-to": redirectTo.optional()}); export const flowDSLStageRegular = z.record(z.string(), z.object({actions: z.array(systemAction), expect: expect})); export const confirmationAction = z.object({notify: z.object({users: z.array(userName), methods: notifyMethods, kind: z.literal("confirmation")})}); export const flowDSLStageConfirmation = z.record(z.string(), z.object({actions: z.array(confirmationAction), expect: z.object({})})); export const flowDSLStage = z.union([flowDSLStageRegular, flowDSLStageConfirmation]); export const dslVersion = z.string(); export const allowRejections = z.boolean(); export const timeOfDay = z.string(); export const reminderRelativeTime = z.object({time_of_day: timeOfDay, time_zone: z.string()}); export const reminderRepeat = z.enum(["never", "periodically"]); export const reminderDate = z.union([z.object({type: z.literal("absolute"), date_time: z.string()}), z.object({type: z.literal("relative"), days_delay: z.number().int().gte(1).lte(365), time: reminderRelativeTime.optional(), repeat: reminderRepeat.optional()})]); export const automaticReminderMethod = z.enum(["email", "sms", "email-and-sms"]); export const automaticReminder = z.object({"user-name": userName, date: reminderDate, method: automaticReminderMethod}); export const flowDSL = z.object({stages: z.array(flowDSLStage), "dsl-version": dslVersion, "allow-rejections": allowRejections.optional(), "automatic-reminders": z.array(automaticReminder).optional()}); export const documentID = z.string(); export const userIdType = z.enum(["email", "phone_number", "user_id", "full_name"]); export const onfidoMethod = z.enum(["document", "document_and_photo", "document_and_video", "document_and_motion"]); export const onfidoCountry = z.string(); export const onfidoDocumentType = z.enum(["national_identity_card", "driving_licence", "passport", "residence_permit"]); export const onfidoDocumentCapture = z.enum(["mobile_only", "mobile_or_desktop"]); export const onfidoAdditionalReportInfo = z.enum(["driving_licence_info", "address_info"]); export const nOUiLocale = z.enum(["nb", "en"]); export const uiLocale = z.enum(["nb-NO", "en-GB", "da-DK", "sv-SE", "fi-FI", "sv-FI"]); export const tupasBank = z.enum(["nordea", "opbank", "danske", "handelsbanken", "aland", "sbank", "aktia", "popbank", "savingsbank", "omasp", "mobileid"]); export const authenticationConfiguration = z.union([z.object({provider: z.literal("sms_otp"), max_failures: z.number().int().gte(0).lte(255), attempts_per_sms: z.number().int().gte(1).lte(5).optional()}), z.object({provider: z.literal("onfido"), max_failures: z.number().int().gte(0).lte(255), method: onfidoMethod.optional(), allowed_document_countries: z.array(onfidoCountry).optional(), allowed_document_types: z.array(onfidoDocumentType).optional(), document_capture: onfidoDocumentCapture.optional(), additional_report_info: onfidoAdditionalReportInfo.optional(), email: z.string().optional()}), z.object({provider: z.literal("se_bankid"), max_failures: z.number().int().gte(0).lte(255)}), z.object({provider: z.literal("dk_mitid"), max_failures: z.number().int().gte(0).lte(255)}), z.object({provider: z.literal("no_bankid"), max_failures: z.number().int().gte(0).lte(255), ui_locale: nOUiLocale.optional()}), z.object({provider: z.literal("ftn"), max_failures: z.number().int().gte(0).lte(255), ui_locale: uiLocale.optional(), force_bank: tupasBank.optional()})]); export const userConfiguration = z.object({id: z.string(), id_type: userIdType, auth_to_view: authenticationConfiguration.optional(), auth_to_view_archived: authenticationConfiguration.optional()}); export const message = z.string(); export const processParameters = z.object({documents: z.record(z.string(), documentID), users: z.record(z.string(), userConfiguration), messages: z.record(z.string(), message)}); export const callbackUrl = z.string(); export const flowCallbackAuthType = z.enum(["none", "basic"]); export const callbackVersion = z.literal("v1"); export const getCallback = z.object({url: callbackUrl, auth_type: flowCallbackAuthType, version: callbackVersion}); export const uTCTime = z.string(); export const userID = z.string(); export const folderID = z.string(); export const getDraft = z.object({id: z.string(), title: z.string().optional(), process: flowDSL.optional(), process_parameters: processParameters.optional(), callback: getCallback.optional(), created: uTCTime, author_id: userID, folder_id: folderID, duration: z.object({days: z.number().int().gte(0).lte(365).optional(), hours: z.number().int().gte(0).lte(8760).optional(), minutes: z.number().int().gte(0).lte(525600).optional()}).optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Create a Flow draft * * POST /experimental/flow/drafts * * Also accepts auth: OAuth2.0, SessionCookie + XToken */ export const createApiExperimentalFlowDrafts = async (args: CreateApiExperimentalFlowDraftsArgs, options?: CreateApiExperimentalFlowDraftsOptions) => { const {baseUrl = "https://scrive.com", fetch: fetchFn = fetch, OAuth10} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json;charset=utf-8") headers.set('accept', "application/json;charset=utf-8") if (OAuth10 !== undefined) headers.set('authorization', `oauth1 ${OAuth10}`) const url = `${baseUrl.replace(/\/+$/, '')}/experimental/flow/drafts${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(getDraft, await res.json()) };