Saltar al contenido
Desarrolladores

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.

El SDK se entrega como el tarball binarybridges-bb-sign-sdk-0.1.0.tgz, generado con npm pack. Instálelo en su proyecto:

Ventana de terminal
npm install ../path/to/binarybridges-bb-sign-sdk-0.1.0.tgz

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.

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.

import { verifyWebhookSignature } from '@binarybridges/bb-sign-sdk/webhooks'

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.

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.

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.

X-BBSign-Event-Id es estable entre reintentos y reentregas. La entrega es al menos una vez; use este valor como clave de idempotencia.

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.

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.

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.

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.

  • 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.
Ventana de terminal
cd sdk-js
npm install
npm run generate # regenerates src/generated from docs/api/openapi.json
npm run build
npm test
./verify.sh # the CI gate: drift, browser-bundle refusal, any-leaks, pack contents
npm pack # produces binarybridges-bb-sign-sdk-0.1.0.tgz

src/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.