import { z } from 'zod'; import type { ConnectorConnection } from '@substrat-run/kernel'; /** * A thin, typed client over the Scrive eSign v2 endpoints. * * Every call goes through the connection's `fetch`, never a global one: that is * what gets it a timeout, an egress policy, and health recorded against the * right connection. Module code cannot reach any of this — boundary-lint bans * `fetch` outright — and a connector is host code. * * **The shapes here were verified against `api-testbed.scrive.com`, not just the * docs.** The first version of this file was written from the documentation and * was wrong in three ways a live call exposed at once (auth scheme, the upload * encoding, the create-response shape). Each is called out below where it bit. */ export declare const SCRIVE_TESTBED = "https://api-testbed.scrive.com"; export declare const SCRIVE_PRODUCTION = "https://scrive.com"; /** * A Scrive connection's credential — OAuth1 "personal access credentials". * * NOT OAuth2 bearer, which the first version assumed. Scrive's UI labels these * "Client credentials" and "Token credentials", which reads like two schemes but * is one: the four parts combine into a PLAINTEXT OAuth signature. The * `oauth2.scrive.com` token endpoint rejects them with `invalid_client` — it is * a different mechanism entirely. */ export declare const scriveSecret: z.ZodObject<{ clientId: z.ZodString; clientSecret: z.ZodString; tokenId: z.ZodString; tokenSecret: z.ZodString; }, z.core.$strip>; export type ScriveSecret = z.infer; /** * An id-bearing response — what `new` / `setfile` / `update` / `start` return. * * `POST /documents/new` returns NO top-level `status` (verified) — only * `/documents/{id}/get` returns the full object. The first version parsed every * response as a full document and would have thrown on call one. So mutation * responses are parsed for their id only, and status is read from `get` — which * is the right design anyway: don't trust a mutation's echo, re-read the truth. */ export declare const scriveDocRef: z.ZodObject<{ id: z.ZodString; }, z.core.$strip>; export type ScriveDocRef = z.infer; /** The full document, as `get` returns it — extra fields ignored. */ export declare const scriveDocument: z.ZodObject<{ id: z.ZodString; status: z.ZodEnum<{ canceled: "canceled"; closed: "closed"; pending: "pending"; preparation: "preparation"; rejected: "rejected"; timedout: "timedout"; }>; parties: z.ZodDefault; is_signatory: z.ZodOptional; signatory_role: z.ZodOptional; sign_time: z.ZodOptional>; authentication_method_to_sign: z.ZodOptional; fields: z.ZodOptional>>; }, z.core.$strip>>>; }, z.core.$strip>; export type ScriveDocument = z.infer; /** * The API user and the company it acts for — `GET /api/v2/getprofile`, the cheapest * authenticated read Scrive offers and therefore the probe (#605). * * Verified against the testbed: the endpoint is `/api/v2/getprofile`, NOT * `/api/v2/user/getprofile` (404), and a bad credential answers `401` with a * **plain-text** body rather than Scrive's usual JSON error envelope — which * {@link asJson} already handles by keeping the raw slice, so the operator reads * "No valid access credentials were provided" instead of a bare status. * * `company.companyid` is what `externalAccountRef` means for this provider, which is * what lets a probe answer "these keys are for a different Scrive account than the one * this connection was made for". Extra fields ignored — the response carries a large * company-settings object this deliberately does not learn. */ export declare const scriveProfile: z.ZodObject<{ id: z.ZodString; email: z.ZodDefault; fstname: z.ZodDefault; sndname: z.ZodDefault; role: z.ZodOptional; company: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>; export type ScriveProfile = z.infer; /** * One row of `GET /api/v2/documents/list` — the account's documents, newest first. * * Only the fields a console shows are parsed; the full row carries ~40 more. This is * the LIVE view: the dispatch ledger knows what the platform sent, this knows what the * provider currently holds, and the two answer different questions. */ export declare const scriveDocumentSummary: z.ZodObject<{ id: z.ZodString; title: z.ZodDefault; status: z.ZodString; ctime: z.ZodOptional; mtime: z.ZodOptional; }, z.core.$strip>; export type ScriveDocumentSummary = z.infer; export declare const scriveDocumentList: z.ZodObject<{ total_matching: z.ZodDefault; documents: z.ZodDefault; status: z.ZodString; ctime: z.ZodOptional; mtime: z.ZodOptional; }, z.core.$strip>>>; }, z.core.$strip>; export type ScriveDocumentList = z.infer; export interface ScriveParty { /** Display name for the signing page. */ name: string; email?: string; /** * Mobile number for an SMS invitation (#687). * * Beside `email` rather than instead of it because the engine's `partyContact` * carries either, and a party reachable only by phone is a party this connector * would otherwise have to refuse. Passed through to the provider's `mobile` * field and, like the address above, never persisted by us. */ mobile?: string; /** * Swedish personnummer, when the sender happens to know it. * * **Optional even for BankID**, which is the whole finding of * [#687](https://github.com/substrat-run/substrat/issues/687): what Scrive * validates on `start` is that the party *has* a `personal_number` field, not * that it holds a value. `update` below therefore sends an EMPTY one for every * `se_bankid` party, and the signatory completes it at signing time. * * When it is supplied it is passed THROUGH to the provider and never persisted * by us: it is `direct` PII, and `engine-protocol` stores an opaque * `DataSubjectId` as the signatory instead. The provider needs it; our tables * must not have it. */ personalNumber?: string; /** `se_bankid` for Swedish BankID; `standard` otherwise. */ authenticationMethodToSign: 'standard' | 'se_bankid'; /** * The sender/author. Scrive auto-adds the API user as an author party on * `new`; exactly one party across the set must be the author, so the connector * marks the issuing (primary) party as it. Verified: sending an explicit * author party in `update` replaces the auto one. */ isAuthor?: boolean; /** A viewer rather than a signer — an author who does not sign. */ isSignatory?: boolean; } /** * A provider response that was not 2xx, carrying the STATUS as data. * * The status is the difference between two failures that must not be conflated: a * `401`/`403` is Scrive saying "not with these credentials" — a definite answer about the * credential — while a timeout, a 5xx or a DNS failure says nothing about it at all. * A caller that cannot tell them apart must either reject good credentials during a * provider outage or accept bad ones; both are worse than asking. */ export declare class ScriveApiError extends Error { readonly status: number; constructor(message: string, status: number); /** The provider refused the CREDENTIAL, as opposed to failing for its own reasons. */ get refused(): boolean; } export declare class ScriveApi { private readonly conn; private readonly baseUrl; private readonly secret; constructor(conn: ConnectorConnection, baseUrl?: string); /** * The OAuth1 PLAINTEXT authorization header. The signature is * `&` — literally the two secrets joined by `&`, * which is what "PLAINTEXT" means: no HMAC, TLS is the confidentiality. */ private headers; /** * Who these credentials are — the probe (#605). Cheap, read-only, and the only call * that names the ACCOUNT rather than a document, which is what makes it the right * answer to "did I paste the right keys, and for which company?". */ getProfile(): Promise; /** * The account's documents, newest first — the live counterpart to the dispatch * ledger. Bounded by `max` (Scrive's own default is small; the console asks for a * page, never the archive) and used to join the provider's CURRENT status onto the * rows the platform recorded sending. */ listDocuments(opts?: { max?: number; offset?: number; }): Promise; createDocument(): Promise; /** * Attach the PDF. **`multipart/form-data`**, verified — not the raw base64 body * the first version sent. The multipart envelope is built as bytes because the * file is binary and a string body would corrupt it (which is why * `ConnectorRequestInit.body` accepts `Uint8Array`). */ setFile(documentId: string, filename: string, pdf: Uint8Array): Promise; /** Parties, callback URL and title, in one `document=` form field. */ update(documentId: string, patch: { title?: string; parties?: ScriveParty[]; callbackUrl?: string; tags?: { name: string; value: string; }[]; }): Promise; /** Send it. After this the document is `pending` and the parties are invited. */ start(documentId: string): Promise; /** * Current state — the polling path, and the only call that returns `status`. * Webhook ingress (#96) is not on the critical path precisely because this * exists and Scrive's callbacks are unauthenticated anyway. */ get(documentId: string): Promise; /** * The sealed signed PDF — Scrive's own copy with the signing evidence attached, * the thing a customer, a dispute, or an auditor actually asks for. * * `GET /api/v2/documents/{id}/files/main` returns the bytes directly, so this is * the one call that reads the response as an ArrayBuffer rather than JSON. The * sealed file exists only once the document is `closed`; on an open document the * response is the working copy or an error depending on account settings, so the * return path fetches only after `get` reports `closed` (issue #476). */ getMainFile(documentId: string): Promise; } //# sourceMappingURL=api.d.ts.map