import {z} from 'zod' export type OptionalNullableFieldText = string; 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 OptionalNullableFieldCallback = {url: CallbackUrl, auth?: AuthMethod | undefined, version: CallbackVersion}; export type FolderID = string; export type StartDraft = {title?: OptionalNullableFieldText | undefined, process?: FlowDSL | undefined, process_parameters?: ProcessParameters | undefined, callback?: OptionalNullableFieldCallback | undefined, duration?: ({days?: number | undefined, hours?: number | undefined, minutes?: number | undefined}) | undefined, folder_id?: FolderID | undefined}; export type CreateApiExperimentalFlowDraftsDraftIdStartArgs = {draft_id: string, body: StartDraft}; export type CreateApiExperimentalFlowDraftsDraftIdStartOptions = {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 instanceEventAction = z.enum(["approve", "sign", "view", "reject", "redirect"]); export const userID = z.string(); export const flowUserId = z.union([z.object({id_type: z.literal("email"), id: z.string()}), z.object({id_type: z.literal("phone_number"), id: z.string()}), z.object({id_type: z.literal("user_id"), id: userID}), z.object({id_type: z.literal("full_name"), id: z.string()})]); export const instanceUserActionForAuthor = z.object({type: instanceEventAction, user: flowUserId, document: documentID.optional()}); export const instanceState = z.object({available_actions: z.array(instanceUserActionForAuthor)}); export const authId = z.string(); export const authIdentifier = z.enum(["view", "view_archived", "sign"]); export const adminFormId = z.object({id: z.literal("participant_form"), participant_id: z.string()}); export const schema = z.object({}); export const uiSchema = z.object({}); export const formData = z.unknown(); export const invitationKind = z.enum(["email", "sms"]); export const adminApiAction = z.union([z.object({action: z.literal("notify_participant"), participant_id: z.string()}), z.object({action: z.literal("cancel_instance")}), z.object({action: z.literal("prolong_instance"), max_days: z.number().int().gte(1).lte(365)}), z.object({action: z.literal("reset_auth_attempts"), participant_id: z.string(), auth_label: authId.optional()}), z.object({action: z.literal("reset_authentication_attempts"), participant_id: z.string(), authentication: authIdentifier}), z.object({action: z.literal("admin_form"), form_id: adminFormId, schema: schema, ui_schema: uiSchema.optional(), default_value: formData.optional()}), z.object({action: z.literal("complete_redirect"), participant_id: z.string(), url: url}), z.object({action: z.literal("modify_reminder_settings"), participant_id: z.string()}), z.object({action: z.literal("delete_reminder_settings"), participant_id: z.string()}), z.object({action: z.literal("cancel_reminder"), participant_id: z.string()}), z.object({action: z.literal("modify_participant_info"), participant_id: z.string(), invitations_sent: z.array(invitationKind), authentication_modifiable: z.boolean()})]); export const getParticipantTag = z.object({category: z.string(), value: z.string()}); export const deprecatedAuthId = z.string(); export const onfidoReport = z.enum(["document", "document_and_photo", "document_and_video", "document_and_motion"]); export const onfidoApplicantNameField = z.string(); export const day = z.string(); export const mitIDCPR = z.string(); export const mitIdLanguage = z.enum(["en", "da", "kl"]); export const fIPersonalNumber = z.string(); export const standardParameters = z.union([z.object({provider: z.literal("sms_otp"), attempts_per_sms: z.number().int().gte(1).lte(5).optional(), mobile_number: z.string().optional()}), z.object({provider: z.literal("onfido"), report: onfidoReport.optional(), allowed_document_countries: z.array(onfidoCountry).optional(), allowed_document_types: z.array(onfidoDocumentType).optional(), force_cross_device: z.boolean().optional(), additional_report_info: onfidoAdditionalReportInfo.optional(), first_name: onfidoApplicantNameField.optional(), last_name: onfidoApplicantNameField.optional(), date_of_birth: day.optional(), email: z.string().optional()}), z.object({provider: z.literal("se_bank_id"), personal_number: z.string().optional()}), z.object({provider: z.literal("dk_mit_id"), personal_number: mitIDCPR.optional(), company_number: z.string().optional(), language: mitIdLanguage.optional(), employee_login: z.boolean().optional()}), z.object({provider: z.literal("no_bank_id"), personal_number: z.string().optional(), mobile_number: z.string().optional(), allow_substantial_level: z.boolean().optional(), ui_locale: nOUiLocale.optional()}), z.object({provider: z.literal("ftn"), force_bank: tupasBank.optional(), ui_locale: uiLocale.optional(), personal_number: fIPersonalNumber.optional()})]); export const sEPersonalNumber = z.string(); export const nOPersonalNumber = z.string(); export const signParameters = z.union([z.object({provider: z.literal("sms_otp"), attempts_per_sms: z.number().int().gte(1).lte(5).optional(), mobile_number: z.string().optional()}), z.object({provider: z.literal("se_bank_id"), personal_number: sEPersonalNumber.optional()}), z.object({provider: z.literal("dk_mit_id"), personal_number: mitIDCPR.optional(), company_number: z.string().optional(), language: mitIdLanguage.optional(), employee_login: z.boolean().optional()}), z.object({provider: z.literal("no_bank_id"), personal_number: nOPersonalNumber.optional(), mobile_number: z.string().optional(), ui_locale: nOUiLocale.optional()}), z.object({provider: z.literal("onfido"), report: onfidoReport.optional(), allowed_document_countries: z.array(onfidoCountry).optional(), allowed_document_types: z.array(onfidoDocumentType).optional(), force_cross_device: z.boolean().optional(), additional_report_info: onfidoAdditionalReportInfo.optional(), first_name: onfidoApplicantNameField.optional(), last_name: onfidoApplicantNameField.optional(), date_of_birth: day.optional(), email: z.string().optional()}), z.object({provider: z.literal("ftn"), force_bank: tupasBank.optional(), ui_locale: uiLocale.optional(), personal_number: fIPersonalNumber.optional()})]); export const participantAuthentication = z.union([z.object({type: z.literal("standard"), label: deprecatedAuthId.optional(), max_attempts_reached: z.boolean(), parameters: standardParameters}), z.object({type: z.literal("sign"), label: deprecatedAuthId.optional(), max_attempts_reached: z.boolean(), parameters: signParameters})]); export const reminderDeliveryMethod = z.enum(["email", "sms", "email_and_sms"]); export const participantReminderSettings = z.object({date: reminderDate, method: reminderDeliveryMethod}); export const automaticReminderDetail = z.object({date: z.string().optional(), settings: participantReminderSettings.optional()}); export const getParticipant = z.object({participant_id: z.string(), dsl_name: userName, tags: z.array(getParticipantTag), authentications: z.array(participantAuthentication), automatic_reminder: automaticReminderDetail, latest_reminder: z.string().optional()}); export const status = z.enum(["in_progress", "finalising", "canceled", "rejected", "completed", "failed", "timed_out"]); export const uTCTime = z.string(); 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 folderID = z.string(); export const initiator = z.enum(["flow_api", "id_check", "journey_service"]); export const getInstance = z.object({id: z.string(), title: z.string().optional(), process: flowDSL, process_parameters: processParameters, state: instanceState, access_links: z.record(z.string(), url), admin_actions: z.array(adminApiAction), participants: z.array(getParticipant), status: status, started: uTCTime, finished: uTCTime.optional(), last_event: uTCTime, trashed: uTCTime.optional(), callback: getCallback.optional(), author_id: userID, folder_id: folderID, timeout: z.string().optional(), documents: z.array(documentID), initiator: initiator, latest_reminder: z.string().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 }; /** * Start a Flow instance from a draft * * POST /experimental/flow/drafts/{draft_id}/start * * Also accepts auth: OAuth2.0, SessionCookie + XToken */ export const createApiExperimentalFlowDraftsDraftIdStart = async (args: CreateApiExperimentalFlowDraftsDraftIdStartArgs, options?: CreateApiExperimentalFlowDraftsDraftIdStartOptions) => { 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/${encodeURIComponent(String(args.draft_id))}/start${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(getInstance, await res.json()) };