SDK de JavaScript
El SDK de JavaScript conecta sus servicios de Node con la API de bb-sign: gestiona los tokens,
reintenta de forma segura y verifica las firmas de los webhooks. El paquete es
@binarybridges/bb-sign-sdk, versión 0.1.0.
Requiere Node 20 o superior y es solo ESM. Use import; require()
no está soportado.
Instalación
Sección titulada «Instalación»El SDK se entrega como el tarball binarybridges-bb-sign-sdk-0.1.0.tgz, generado con
npm pack. Instálelo en su proyecto:
npm install ../path/to/binarybridges-bb-sign-sdk-0.1.0.tgzInicio rápido
Sección titulada «Inicio rápido»Las credenciales se crean en Configuración, pestaña Credenciales de API, de bb-sign; el secreto se muestra una sola vez, al crearlas. Léalas desde variables de entorno, fuera del código fuente.
import { createClient } from '@binarybridges/bb-sign-sdk'
const client = createClient({ baseUrl: process.env.BBSIGN_BASE_URL, tokenUrl: process.env.BBSIGN_TOKEN_URL, clientId: process.env.BBSIGN_CLIENT_ID, clientSecret: process.env.BBSIGN_CLIENT_SECRET,})
const envelope = await client.envelopes.createEnvelope({ createEnvelopeRequest: { title: 'Q3 Agreement', otpRequired: false, expiresAt: new Date(Date.now() + 72 * 3600 * 1000), signers: [{ email: 'signer@example.com', name: 'A Signer', signOrder: 1 }], },})
console.log('Created', envelope.id)Los tokens se obtienen, se guardan en caché y se renuevan automáticamente. Las llamadas
concurrentes comparten una sola solicitud de token. El cliente expone envelopes, documents y
verification.
Listar por páginas
Sección titulada «Listar por páginas»for await (const envelope of client.eachPage( (page) => client.envelopes.listEnvelopes({ page, size: 50 }), (p) => p.content,)) { console.log(envelope.title)}Las páginas se obtienen bajo demanda, de modo que recorrer miles de sobres no los carga todos en memoria.
Verificar un webhook
Sección titulada «Verificar un webhook»import { verifyWebhookSignature } from '@binarybridges/bb-sign-sdk/webhooks'Capturar el cuerpo original
Sección titulada «Capturar el cuerpo original»La firma cubre los bytes exactos recibidos. Los frameworks de Node más usados interpretan el JSON por defecto y entregan un objeto; volver a serializarlo produce bytes distintos y la verificación falla aunque el objeto sea correcto. Capture el cuerpo original como se muestra a continuación.
Express
import express from 'express'
app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf }, // keep the bytes}))
app.post('/webhooks/bb-sign', (req, res) => { if (!verifyWebhookSignature(req.get('X-BBSign-Signature'), req.rawBody, SECRET)) { return res.sendStatus(401) } const event = JSON.parse(req.rawBody.toString('utf8')) res.sendStatus(200) // ack first, work after void handle(event)})Next.js (App Router)
export async function POST(request) { const rawBody = await request.text() // text(), not json() if (!verifyWebhookSignature(request.headers.get('x-bbsign-signature'), rawBody, SECRET)) { return new Response(null, { status: 401 }) } const event = JSON.parse(rawBody) return new Response(null, { status: 200 })}Fastify: registre un parser de content-type que conserve el buffer:
fastify.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => { req.rawBody = body done(null, JSON.parse(body.toString('utf8')))})El helper acepta un string o un Buffer. Un objeto ya interpretado produce un error de
tipos, en lugar de convertirse en silencio con JSON.stringify, que daría bytes distintos de los
firmados.
Resultado booleano, sin excepciones
Sección titulada «Resultado booleano, sin excepciones»El helper recibe datos que pueden venir de un atacante. Por eso devuelve siempre un booleano: una excepción produciría un 500 en su ruta de webhook y revelaría por qué falló la verificación.
Responder de inmediato y procesar después
Sección titulada «Responder de inmediato y procesar después»Devuelva 2xx en cuanto la firma sea válida. bb-sign reintenta con una secuencia que abarca aproximadamente 15 horas, y tras 3 secuencias consecutivas agotadas desactiva el endpoint. Procese el evento de forma asíncrona para que la respuesta no dependa de la carga.
Eliminar duplicados por id de evento
Sección titulada «Eliminar duplicados por id de evento»X-BBSign-Event-Id es estable entre reintentos y reentregas. La entrega es al menos una vez; use
este valor como clave de idempotencia.
Rotación del secreto
Sección titulada «Rotación del secreto»Mientras la ventana de rotación está abierta, el encabezado lleva dos valores v1= y cualquiera de
los dos verifica. Esto le permite desplegar el nuevo secreto sin interrupción, sin configuración
adicional.
Probar el handler
Sección titulada «Probar el handler»La verificación depende del tiempo: una firma con más de 300 s de antigüedad se rechaza, de modo que un fixture fijo deja de ser válido. Fije el reloj en las pruebas:
verifyWebhookSignature(header, rawBody, secret, { nowSeconds: 1760000000 })Un receptor detrás de una cola puede ampliar la ventana con toleranceSeconds.
Reintentos
Sección titulada «Reintentos»Las solicitudes GET se reintentan ante errores 5xx y fallos de transporte, con backoff
exponencial y jitter completo.
Un POST cuya respuesta no se recibió no se reintenta. Si createEnvelope agota el tiempo de
espera, el resultado es desconocido: el sobre puede existir ya, y un reintento crearía un segundo
acuerdo enviado a las mismas personas. La API no usa claves de idempotencia. El SDK de Java se
comporta igual.
Si una creación agota el tiempo de espera, liste antes de reintentar:
try { await client.envelopes.createEnvelope({ createEnvelopeRequest: request })} catch (error) { if (error.statusCode === 0) { // Never reached bb-sign, or the answer was lost. Check before sending again. }}429 y 503 se reintentan también en POST, porque indican que el servidor no procesó la
solicitud.
Errores
Sección titulada «Errores»Cada fallo lanza un BbSignError con un statusCode. 0 indica que la solicitud no obtuvo
respuesta.
| Estado | Significado |
|---|---|
| 400 | Solicitud mal formada o con errores de validación |
| 401 | Credenciales rechazadas. El SDK renueva y reintenta una vez antes de reportarlo |
| 403 | Autenticado, pero esta credencial no tiene el permiso de la organización |
| 404 | El recurso no existe, o pertenece a otra organización o a un espacio de trabajo que la credencial no puede ver. No se distinguen, de forma intencional |
| 409 | El recurso no está en un estado que permita la operación |
| 429 | Una cuota de la organización está agotada |
El error contiene un estado y un mensaje, sin el objeto Response: registrarlo en un log no
expone su encabezado Authorization. Los mensajes nunca incluyen su secreto, aunque la respuesta
de bb-sign lo contenga. Errores incluye la tabla completa y el cuerpo
del error.
Garantías de seguridad
Sección titulada «Garantías de seguridad»- Credenciales fuera de los logs. El SDK no tiene hook de logging que pueda registrarlas.
- TLS siempre verificado. La verificación de certificados no se puede desactivar.
- Escrituras sin duplicados. Una escritura con resultado desconocido no se reintenta (ver Reintentos).
- Secreto de webhooks solo en el servidor. El helper de webhooks no se puede empaquetar para navegador, por diseño.
Compilar desde el código fuente
Sección titulada «Compilar desde el código fuente»cd sdk-jsnpm installnpm run generate # regenerates src/generated from docs/api/openapi.jsonnpm run buildnpm test./verify.sh # the CI gate: drift, browser-bundle refusal, any-leaks, pack contentsnpm pack # produces binarybridges-bb-sign-sdk-0.1.0.tgzsrc/generated/ es código generado y versionado: regenérelo en lugar de editarlo.
GENERATION.md, en el código fuente del SDK, documenta la configuración del generador.