Código aberto · MIT · Um produto Montte

Um ponto único de assinatura tipado para cada documento.

A1/PKCS#12, PDF/PAdES, XML-DSig e provedores remotos, tudo nativo de Effect. Troque o assinador, mantenha o programa — navegador ou servidor, erros tipados, segredos Redacted.

Ler a documentação
  • Clicksign
  • Assinafy
  • ZapSign
  • DocuSeal
  • Documenso

Ao vivo · no seu navegador

Assine um PDF real, aqui mesmo.

Sem backend, sem upload, sem SaaS. Solte um PDF, clique onde a assinatura vai, carregue seu A1 (e-CPF / e-CNPJ) e assine com WebCrypto. O documento e a chave privada nunca saem da página.

signaturekit — assinador no navegador
01

Envie seu PDF

Renderizado localmente com pdf.js.

02

Clique para posicionar

Solte o retângulo de assinatura PAdES em qualquer lugar de qualquer página.

03

Assine com seu A1

Seu .pfx e a senha assinam no navegador via WebCrypto.

100% no cliente · WebCrypto · PAdES (AdES-BES)

Loading signer…

Autoassinatura

Posicione uma vez. Assine a pilha inteira.

Vários contratos reais são gerados no seu navegador com @react-pdf/renderer, renderizados no mesmo canvas de posicionamento pdf.js que o assinador usa, e então posicionados e assinados automaticamente numa fila — um documento por vez.

Processando…

Provedores remotos

Um SDK para cada assinador.

Oito provedores de assinatura, um único formato de requisição. Escolha um provedor — a chamada create*SignatureRequest muda, o código da sua aplicação não.

Adaptadores
Cada provedor é um create*SignatureRequest sobre um SignatureHttpClient compartilhado — o código específico do provedor fica fora do seu app.
Erros tipados
Nada lança exceção. Toda falha é um SignatureKitError no canal do Effect, marcado com o provedor e um código literal.
Segredos protegidos
Tokens e chaves de API permanecem em Redacted até a chamada HTTP — nunca logados, nunca vazados.
Formatos próprios por provedor
Cada signer remoto expõe os documentos, destinatários, status e limites que seu provedor realmente suporta.
ProvedorClicksign
clicksign.ts
import * as Alchemy from "alchemy"
import { ClicksignSignatureRequest, providers as clicksignProviders } from "@signature-kit/clicksign"
import { signatureHttpClientLive } from "@signature-kit/http"
import { Effect, Layer, Redacted } from "effect"

export default class Contracts extends Alchemy.Stack<Contracts>()(
  "Contracts",
  {
    providers: Layer.merge(
      clicksignProviders({
        accessToken: Redacted.make(process.env.CLICKSIGN_TOKEN ?? ""),
        environment: "sandbox",
        locale: "pt-BR",
        autoClose: true,
      }),
      signatureHttpClientLive,
    ),
    state: Alchemy.inMemoryState(),
  },
  Effect.gen(function* () {
    return yield* ClicksignSignatureRequest("membership-agreement", {
      title: "Membership agreement",
      documents: [{ fileName: "agreement.pdf", mimeType: "application/pdf", contentBase64: pdfBase64 }],
      recipients: [{ name: "Bruno Lima", email: "bruno@example.com", role: "signer" }],
    })
  }),
) {}
import * as Alchemy from "alchemy"
import { AssinafySignatureRequest, providers as assinafyProviders } from "@signature-kit/assinafy"
import { signatureHttpClientLive } from "@signature-kit/http"
import { Effect, Layer, Redacted } from "effect"

export default class Contracts extends Alchemy.Stack<Contracts>()(
  "Contracts",
  {
    providers: Layer.merge(
      assinafyProviders({
        accountId: process.env.ASSINAFY_ACCOUNT_ID ?? "",
        apiKey: Redacted.make(process.env.ASSINAFY_API_KEY ?? ""),
        environment: "sandbox",
      }),
      signatureHttpClientLive,
    ),
    state: Alchemy.inMemoryState(),
  },
  Effect.gen(function* () {
    return yield* AssinafySignatureRequest("contract", {
      title: "Contract",
      documents: [{ fileName: "contract.pdf", mimeType: "application/pdf", contentBase64: pdfBase64 }],
      recipients: [{ name: "Carla Nunes", email: "carla@example.com" }],
    })
  }),
) {}
import * as Alchemy from "alchemy"
import { ZapSignSignatureRequest, providers as zapSignProviders } from "@signature-kit/zapsign"
import { signatureHttpClientLive } from "@signature-kit/http"
import { Effect, Layer, Redacted } from "effect"

export default class Contracts extends Alchemy.Stack<Contracts>()(
  "Contracts",
  {
    providers: Layer.merge(
      zapSignProviders({
        apiToken: Redacted.make(process.env.ZAPSIGN_API_TOKEN ?? ""),
        environment: "sandbox",
        locale: "pt-br",
      }),
      signatureHttpClientLive,
    ),
    state: Alchemy.inMemoryState(),
  },
  Effect.gen(function* () {
    return yield* ZapSignSignatureRequest("contract", {
      title: "Contract",
      documents: [{ fileName: "contract.pdf", mimeType: "application/pdf", contentBase64: pdfBase64 }],
      recipients: [{ name: "Davi Rocha", email: "davi@example.com" }],
    })
  }),
) {}
import * as Alchemy from "alchemy"
import { DocuSealSignatureRequest, providers as docuSealProviders } from "@signature-kit/docuseal"
import { signatureHttpClientLive } from "@signature-kit/http"
import { Effect, Layer, Redacted } from "effect"

export default class Contracts extends Alchemy.Stack<Contracts>()(
  "Contracts",
  {
    providers: Layer.merge(
      docuSealProviders({
        apiKey: Redacted.make(process.env.DOCUSEAL_API_KEY ?? ""),
        baseUrl: "https://api.docuseal.com",
        submittersOrder: "preserved",
      }),
      signatureHttpClientLive,
    ),
    state: Alchemy.inMemoryState(),
  },
  Effect.gen(function* () {
    return yield* DocuSealSignatureRequest("service-agreement", {
      title: "Service agreement",
      documents: [{ fileName: "agreement.pdf", mimeType: "application/pdf", contentBase64: pdfBase64 }],
      recipients: [{ name: "Ana Silva", email: "ana@example.com", role: "signer" }],
    })
  }),
) {}
import * as Alchemy from "alchemy"
import { DocumensoSignatureRequest, providers as documensoProviders } from "@signature-kit/documenso"
import { signatureHttpClientLive } from "@signature-kit/http"
import { Effect, Layer, Redacted } from "effect"

export default class Contracts extends Alchemy.Stack<Contracts>()(
  "Contracts",
  {
    providers: Layer.merge(
      documensoProviders({
        apiKey: Redacted.make(process.env.DOCUMENSO_API_KEY ?? ""),
        baseUrl: "https://app.documenso.com/api/v2",
      }),
      signatureHttpClientLive,
    ),
    state: Alchemy.inMemoryState(),
  },
  Effect.gen(function* () {
    return yield* DocumensoSignatureRequest("service-agreement", {
      title: "Service agreement",
      documents: [{ fileName: "agreement.pdf", mimeType: "application/pdf", contentBase64: pdfBase64 }],
      recipients: [{ name: "Ana Silva", email: "ana@example.com", role: "approver", routingOrder: 1 }],
    })
  }),
) {}

Sete capacidades

Tudo o que você assina, uma só API.

A1 / PKCS#12 no navegador ou no servidor, PDF·PAdES e XML-DSig, oito provedores remotos, um canal de erros tipado e uma camada React para assinatura no navegador — a mesma ergonomia nativa de Effect em todos os backends.

01

Um só serviço, todos os formatos

inspect, sign e verify partem de um único serviço Signatures — forneça uma layer e os mesmos três acessores conduzem todos os backends.
import { signatures } from "@signature-kit/signatures"
02

A1 no navegador ou no servidor

Carregue um A1 e-CPF / e-CNPJ (.pfx / PKCS#12) uma vez para uma layer Signatures; mesma chamada no servidor ou no navegador, senha guardada em Redacted.
a1SignaturesLayer({ pfx, password: Redacted.make(password) })
03

PDF, PAdES, ICP-Brasil

signPdf sela sob uma política PAdES: pades-icp-brasil para a cadeia ICP brasileira, ou pades-ades.
signPdf({ pdf, policy: "pades-icp-brasil" })
04

XML-DSig, por referência

signXml escreve um XML-DSig envelopado sobre um nó referenciado; verifyXml não precisa de layer — a chave viaja no documento.
signXml({ xml, referenceId: "nfe-1" })
05

Provedores remotos, um canal tipado

Clicksign, Assinafy, ZapSign, DocuSeal e Documenso recebem cada um um create*SignatureRequest sobre um único cliente HTTP.
ClicksignSignatureRequest("contract", props)
06

Um canal de erros tipado

Nada lança exceção. Toda falha é um SignatureKitError carregando um de 18 códigos literais, capturado no canal de erros do Effect.
Effect.catchTag("SignatureKitError", handle)
07

Assine PDF no navegador

@signature-kit/pdf cuida de leitura de arquivo, estado de posicionamento e assinatura PDF A1 no navegador. O signer ao vivo renderiza a UI da app ao redor dessa fronteira tipada.
import { signPdfSignatureField } from "@signature-kit/pdf/workflow"

Dois passos

Sua primeira assinatura, de ponta a ponta.

Instale o núcleo e o assinador A1, depois construa uma layer e execute um programa que inspeciona o certificado e assina.

011. Instale
bun add @signature-kit/signatures @signature-kit/a1
npm install @signature-kit/signatures @signature-kit/a1
pnpm add @signature-kit/signatures @signature-kit/a1
yarn add @signature-kit/signatures @signature-kit/a1

@signature-kit/signatures é o ponto de entrada tipado. Cada assinador e formato é instalado apenas quando você precisa dele: /a1/pdf/xml

022. Faça sua primeira chamadafirst-signature.ts
import { a1SignaturesLayer } from "@signature-kit/a1/signer"
import { signatures } from "@signature-kit/signatures"
import { Effect, Redacted } from "effect"

const layer = a1SignaturesLayer({
  pfx,
  password: Redacted.make(process.env.A1_PASSWORD ?? ""),
})

const program = Effect.gen(function* () {
  const identity = yield* signatures.inspect()

  const artifact = yield* signatures.sign({
    content,
    algorithm: "rsa-sha256",
  })

  return { identity, artifact }
}).pipe(Effect.provide(layer))

const { identity, artifact } = await Effect.runPromise(program)

runPromiseresolve para aidentityanalisada e oartifactassinado. A partir daqui, troque a layer A1 por um formato —signPdfousignXml— ou entregue o programa a um provedor remoto.

Apoiado por

Quando você quiser

Implemente a camada de assinatura uma vez.

Código aberto, licença MIT, nativo de Effect. A1, PDF, XML e provedores remotos sob um único programa tipado — cada falha um valor no qual você pode fazer match.

Início rápido