Quickstart
Get from zero to receiving encrypted webhooks in under five minutes. This guide walks you through creating an account, generating encryption keys, setting up a webhook endpoint, and consuming messages.
The 30-second version
If you already have an account and keys, here are the three commands that matter:
# 1. Create an API key
curl -X POST https://nexus-broker.dev/api/keys \
-H "Authorization: Bearer <firebase-id-token>" \
-H "Content-Type: application/json" \
-d '{"name": "my-key"}'
# 2. Create a webhook with your public key
curl -X POST https://nexus-broker.dev/api/webhooks \
-H "x-api-key: nxb_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "stripe-webhooks",
"publicKey": "'"$(cat public.pem)"'"
}'
# 3. Dequeue and decrypt
curl https://nexus-broker.dev/api/webhooks/<id>/dequeue \
-H "x-api-key: nxb_your_api_key"Step 1 — Create an account
Navigate to the /dashboard page and sign in with your Google or GitHub account. Nexus Broker uses Firebase Authentication, so there are no passwords to manage. After signing in you'll land on your dashboard where you can manage API keys, webhooks, and view usage statistics.
Step 2 — Generate a keypair
Nexus Broker encrypts every webhook payload with your public key before storing it. Only someone with the corresponding private key can decrypt the contents. You choose between two algorithms:
- RSA-2048 — The industry standard. Widely compatible, supported by every crypto library. No forward secrecy.
- X25519 — Modern elliptic curve. Smaller keys, faster operations, provides forward secrecy through ephemeral key exchange. Recommended for new projects.
Generate your keypair locally using OpenSSL. Never upload your private key.
RSA-2048
# Generate a 2048-bit RSA private key openssl genrsa -out private.pem 2048 # Extract the public key openssl rsa -in private.pem -pubout -out public.pem
X25519
# Generate an X25519 private key openssl genpkey -algorithm X25519 -out private.pem # Extract the public key openssl pkey -in private.pem -pubout -out public.pem
Tip: Store your private key securely. If you lose it, you lose access to all encrypted messages. Consider using a secrets manager like AWS Secrets Manager, HashiCorp Vault, or a hardware security module for production workloads.
Step 3 — Create an API key
API keys authenticate requests to the Nexus Broker management and queue APIs. You can create them from the dashboard or programmatically:
curl -X POST https://nexus-broker.dev/api/keys \
-H "Authorization: Bearer <firebase-id-token>" \
-H "Content-Type: application/json" \
-d '{
"name": "production-key"
}'The response returns your API key prefixed with nxb_. Store it securely — it won't be shown again.
{
"id": "key_abc123",
"name": "production-key",
"key": "nxb_Kj3m9x...Rq7w",
"createdAt": "2025-01-15T10:30:00Z"
}Step 4 — Create a webhook
A webhook represents an ingestion endpoint. When you create one, Nexus Broker generates a unique slug that external services can POST to. You'll provide your public key so payloads get encrypted on arrival.
curl -X POST https://nexus-broker.dev/api/webhooks \
-H "x-api-key: nxb_Kj3m9x...Rq7w" \
-H "Content-Type: application/json" \
-d '{
"name": "stripe-payments",
"publicKey": "'"$(cat public.pem)"'",
"algorithm": "RSA-2048"
}'{
"id": "wh_def456",
"name": "stripe-payments",
"slug": "a7b3c9f2e1",
"url": "https://nexus-broker.dev/wh/a7b3c9f2e1",
"algorithm": "RSA-2048",
"createdAt": "2025-01-15T10:35:00Z"
}The url field is the endpoint you give to external services. Any HTTP POST to this URL will be encrypted and queued.
Step 5 — Send a webhook
Send any JSON payload to your webhook URL. The ingestion endpoint is public and unauthenticated — it accepts any valid JSON body.
curl -X POST https://nexus-broker.dev/wh/a7b3c9f2e1 \
-H "Content-Type: application/json" \
-d '{
"event": "payment.completed",
"amount": 4999,
"currency": "usd",
"customer": "cus_abc123"
}'{
"id": "msg_ghi789",
"sequence": 1,
"status": "queued",
"createdAt": "2025-01-15T10:40:00Z"
}Step 6 — Consume messages
Use the dequeue endpoint to fetch and lock the next available message. The response includes the encrypted payload, which you decrypt with your private key.
curl -X POST https://nexus-broker.dev/api/webhooks/wh_def456/dequeue \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
{
"message": {
"id": "msg_ghi789",
"sequence": 1,
"status": "locked",
"lockExpiresAt": "2025-01-15T10:45:00Z",
"createdAt": "2025-01-15T10:40:00Z"
},
"encrypted": {
"ciphertext": "base64-encoded-ciphertext...",
"iv": "base64-encoded-iv",
"tag": "base64-encoded-tag",
"encryptedKey": "base64-encoded-encrypted-aes-key",
"algorithm": "RSA-2048-AES-256-GCM"
},
"headers": {
"content-type": "application/json"
}
}Decrypting in Node.js
import { privateDecrypt, createDecipheriv } from "node:crypto";
import { readFileSync } from "node:fs";
const privateKey = readFileSync("private.pem", "utf-8");
function decrypt(encrypted) {
// 1. Decrypt the AES key with RSA private key
const aesKey = privateDecrypt(
{ key: privateKey, padding: 1, oaepHash: "sha256" },
Buffer.from(encrypted.encryptedKey, "base64"),
);
// 2. Decrypt the payload with AES-256-GCM
const decipher = createDecipheriv(
"aes-256-gcm",
aesKey,
Buffer.from(encrypted.iv, "base64"),
);
decipher.setAuthTag(Buffer.from(encrypted.tag, "base64"));
const decrypted = Buffer.concat([
decipher.update(Buffer.from(encrypted.ciphertext, "base64")),
decipher.final(),
]);
return JSON.parse(decrypted.toString("utf-8"));
}Decrypting in Python
import base64, json
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives import hashes
with open("private.pem", "rb") as f:
private_key = load_pem_private_key(f.read(), password=None)
def decrypt(encrypted):
# 1. Decrypt the AES key
aes_key = private_key.decrypt(
base64.b64decode(encrypted["encryptedKey"]),
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
# 2. Decrypt the payload with AES-256-GCM
aesgcm = AESGCM(aes_key)
plaintext = aesgcm.decrypt(
nonce=base64.b64decode(encrypted["iv"]),
data=(
base64.b64decode(encrypted["ciphertext"])
+ base64.b64decode(encrypted["tag"])
),
)
return json.loads(plaintext)Step 7 — Commit or error
After successfully processing a message, commit it to remove it from the queue:
# Acknowledge successful processing curl -X POST https://nexus-broker.dev/api/webhooks/wh_def456/messages/msg_ghi789/commit \ -H "x-api-key: nxb_Kj3m9x...Rq7w"
If processing fails, report the error. The message will be released back to the queue for retry (up to the maximum retry count):
# Report a processing failure
curl -X POST https://nexus-broker.dev/api/webhooks/wh_def456/messages/msg_ghi789/error \
-H "x-api-key: nxb_Kj3m9x...Rq7w" \
-H "Content-Type: application/json" \
-d '{"reason": "Database connection timeout"}'Warning: If you neither commit nor error a message, its lock will expire after the configured timeout (default 300 seconds). The message will return to the queue and may be dequeued again, leading to duplicate processing. Always commit or error every message you dequeue.
Next steps
- Learn how the system architecture works under the hood
- Understand the encryption and security model
- Explore the full API reference for advanced usage
- Read about queue behavior and delivery guarantees