Ce guide couvre comment porter vos credentials DZBuild API en sécurité dans votre application — dev local, staging, production. Que vous construisiez une vitrine Node.js, un back-office Laravel/Symfony, un pipeline Python, un microservice Go ou une fonction edge serverless, les règles sont les mêmes.
Avant de commencer
Votre clé doit être inscrite au pilote. L'API v1 est réservée au pilote en production. Une clé que DZBuild n'a pas inscrite renvoie
403 forbidden"API is in pilot mode; key not enrolled"à chaque appel, aussi correcte que soit votre configuration.Pointez toujours
DZBUILD_API_BASEsurhttps://api.dzbuild.app/v1.dzbuild.com/api/v1/...n'est qu'un alias de la même API : certains chemins peuvent y être bloqués, et il ne bénéficie pas du cache de lecture de 30 secondes. Le rate limiting par clé et le rejeu d'idempotence s'appliquent sur les deux hôtes.
Ce que vous devez stocker
Pour une intégration vitrine / back-office typique :
Variable | Exemple | Notes |
|
| Le bearer token complet. Traitez-le comme un mot de passe. |
|
| URL de base. En prod, utilisez cet hôte, jamais |
| 64 caractères hex minuscules | Secret par webhook retourné à l'enregistrement. Sans préfixe. Voir Secrets de webhooks pour ce qu'il peut et ne peut pas faire aujourd'hui. |
Anatomie du bearer token
Les marchands le tronquent en permanence, autant l'expliciter. Le bearer token d'une clé plateforme est :
dzpk_live_<14 hex>.<48 hex>
73 caractères, tous en minuscules après le préfixe, avec un seul point au milieu. La partie dzpk_live_… avant le point est le key id — un identifiant, pas un credential. Authorization: Bearer a besoin de la chaîne entière de 73 caractères.
Se tromper là-dessus fait diverger les deux couches, ce qui est un diagnostic utile :
Un token sans aucun point →
401 unauthorized "Invalid bearer format".Un token bien formé mais inconnu / révoqué →
401 unauthorized "Invalid or revoked API key".
Pour le flow clé publique (signups / events depuis un client public) :
Variable | Exemple | Notes |
|
| Safe à shipper dans le code client (l'id est public, le secret non) |
| 64 caractères hex minuscules, sans préfixe | Server-side seulement — sert à HMAC-signer chaque body de requête |
Fichiers .env
Le pattern le plus simple :
# .env (à la racine, gitignored) DZBUILD_API_KEY=dzpk_live_3f9c1b7a4e02d5.4d1c8a90f7b23e6510ac7fd9b48e2c31a05f6d7e8b9c0a1d DZBUILD_API_BASE=https://api.dzbuild.app/v1 DZBUILD_WEBHOOK_SECRET=fc9b5f0b51b4a93c1d6f8e29b6a2e30c7c2c44a4f2a6c8d8e0e1b9d4f6c1a8b3
# .gitignore .env .env.local .env.*.local
Committez un .env.example avec des placeholders pour que les autres devs sachent quelles vars définir :
# .env.example (committé) DZBUILD_API_KEY=dzpk_live_REPLACE_ME DZBUILD_API_BASE=https://api.dzbuild.app/v1 DZBUILD_WEBHOOK_SECRET=REPLACE_ME_64_HEX
Charger .env par langage
Node.js / Next.js / Express
// next.config.js — Next.js charge .env, .env.local, .env.production automatiquement // Pour Node simple : import 'dotenv/config'; const key = process.env.DZBUILD_API_KEY;
Python
# pip install python-dotenv from dotenv import load_dotenv import os load_dotenv() key = os.environ['DZBUILD_API_KEY']
PHP / Laravel
// Laravel charge .env auto
$key = env('DZBUILD_API_KEY');// PHP simple :
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
$key = $_ENV['DZBUILD_API_KEY'];
Go
// go get github.com/joho/godotenv
godotenv.Load()
key := os.Getenv("DZBUILD_API_KEY")
Flutter / mobile
Ne shippez pas la clé plateforme dans votre app mobile. À la place :
Votre app mobile appelle votre back-end.
Votre back-end détient la clé et proxie vers DZBuild.
Si vous avez besoin que le client mobile parle directement à DZBuild (ex. pour signups), utilisez le flow clé publique — seul l'id de la clé publique est shipped, jamais le secret plateforme. Voir Endpoints clé publique.
Environnements de production
En prod, n'utilisez pas un fichier .env. Utilisez le secret store natif de la plateforme :
Vercel / Netlify / Cloudflare Pages
Projet → Settings → Environment Variables DZBUILD_API_KEY = dzpk_live_... DZBUILD_API_BASE = https://api.dzbuild.app/v1 DZBUILD_WEBHOOK_SECRET = <64-char lowercase hex>
Marquez les vars prod-only en scope « Production ». Les vars staging en « Preview » ou env séparé.
Cloudflare Workers
wrangler secret put DZBUILD_API_KEY # (collez la valeur quand demandé)
Disponible dans le worker comme env.DZBUILD_API_KEY (avec [vars] déclaré dans wrangler.toml).
AWS Lambda / API Gateway
Utilisez AWS Secrets Manager ou Parameter Store :
import boto3, json
secret = json.loads(
boto3.client('secretsmanager').get_secret_value(SecretId='dzbuild/prod')['SecretString']
)
key = secret['DZBUILD_API_KEY']
Ne stockez pas la clé dans Lambda env vars en clair (ils apparaissent dans CloudTrail logs). Référencez le secret manager.
Docker / Docker Compose
# docker-compose.yml
services:
app:
image: yourapp:latest
env_file:
- .env.production # NON committé
environment:
- NODE_ENV=production
Pour Kubernetes, utilisez un Secret :
apiVersion: v1 kind: Secret metadata: name: dzbuild-creds type: Opaque stringData: DZBUILD_API_KEY: dzpk_live_... DZBUILD_WEBHOOK_SECRET: <64-char lowercase hex>
Référence dans le deployment :
envFrom:
- secretRef:
name: dzbuild-creds
GitHub Actions
# .github/workflows/deploy.yml
env:
DZBUILD_API_KEY: ${{ secrets.DZBUILD_API_KEY }}
Définissez le secret dans Repo → Settings → Secrets and variables → Actions.
Clés dev vs production
Créez toujours deux clés séparées :
Une nommée
devoustaging— utilisée dans.env.local/ devUne nommée
production— utilisée seulement en prod réelle
Si la clé dev fuit, vous ne brûlez que dev. Les données prod restent intactes.
Comment obtenir une clé, concrètement
Il n'existe aucune page « Developer → API Keys » dans le dashboard marchand. Pendant le pilote :
Votre première clé est émise par DZBuild sur demande — contactez le support ou votre gestionnaire de compte. Demandez une clé nommée d'après l'environnement où elle vivra (
myapp-dev,myapp-prod), et son inscription au pilote.Les clés suivantes, vous pouvez les créer vous-même à partir d'une clé existante :
bash curl -X POST 'https://api.dzbuild.app/v1/keys' \ -H "Authorization: Bearer $DZBUILD_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"type":"platform","name":"myapp-dev"}' # HTTP 200 → { "data": { "key_id", "bearer_token", "signing_secret", "note" } }
La nouvelle clé hérite tel quel du tier de rate limit et du flag pilote de la clé appelante.
Les scopes ne sont pas sélectionnables. POST /v1/keys n'accepte que type et name. Une clé plateforme reçoit toujours l'ensemble par défaut complet — store:read, store:write, products:read, products:write, orders:read, orders:write, customers:read, landing_pages:read, landing_pages:write, webhooks:read, webhooks:write, usage:read. Une clé publique reçoit toujours signups:write et events:write. La séparation dev / prod vient de l'usage de deux clés différentes, pas d'une restriction de permissions.
Secrets de webhooks
Quand vous enregistrez un webhook, la réponse inclut un secret :
curl -X POST 'https://api.dzbuild.app/v1/webhooks' \
-H "Authorization: Bearer $DZBUILD_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"url": "https://yourapp.com/webhook",
"events": ["order.created", "order.confirmed"]
}'
Réponse — HTTP 200, et seulement trois champs :
{
"data": {
"id": 42,
"secret": "fc9b5f0b51b4a93c1d6f8e29b6a2e30c7c2c44a4f2a6c8d8e0e1b9d4f6c1a8b3",
"note": "Save the secret now — it is not retrievable after this response."
},
"meta": { "request_id": "...", "api_version": "v1" }
}
Le secret est une chaîne nue de 64 caractères hex minuscules — il n'y a pas de préfixe dzwh_sec_. Il est affiché une fois ; sauvez-le immédiatement dans votre coffre. Si vous le perdez, supprimez le webhook et enregistrez-en un autre.
⚠️ Attention — Vous ne pouvez pas encore vérifier les livraisons API v1 avec ce secret
L'API v1 ne signe pas ses livraisons avec le secret par webhook ci-dessus : du code HMAC écrit contre DZBUILD_WEBHOOK_SECRET rejette donc toutes les livraisons authentiques. Conservez le secret pour plus tard, mais authentifiez les livraisons v1 autrement — et relisez l'enregistrement via l'API avant d'agir dessus.
L'explication complète, avec du code de vérification qui fonctionne (pour l'addon Webhooks marchand), se trouve dans Vérifier les signatures — une seule source de vérité, en 4 langages.
Flow clé publique (signups / events)
Le flow clé publique existe pour des endpoints à scope étroit (signup tracking, event tracking) où vous ne voulez pas de clé plateforme dans la boucle. Seul l'id de la clé publique n'est pas secret — le secret de signature reste sur votre back-end.
⚠️ Attention — N'appelez pas /v1/signups directement depuis un navigateur
Trois choses cassent un appel navigateur direct aujourd'hui :
Pas de CORS. Le preflight passe, mais la réponse du POST réel ne porte aucun
Access-Control-Allow-Origin: le navigateur la jette.Idempotency-Keyest obligatoire sur chaque POST, et c'est facile à oublier côté client.crypto.randomUUID()n'est pas un nonce valide. Le nonce doit faire exactement 32 caractères hex minuscules ; un UUID de 36 caractères avec tirets renvoie401 unauthorized "Invalid nonce format".
Passez par votre propre back-end à la place — le pattern ci-dessous.
// Navigateur : parlez à VOTRE endpoint, jamais à api.dzbuild.app
await fetch('/api/track-signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, external_user_id: externalId })
});
// Votre back-end (Node) : détient le secret de signature, signe et relaie.
import crypto from 'node:crypto';const KEY_ID = process.env.DZBUILD_PUBLIC_KEY_ID;
const SECRET = process.env.DZBUILD_SIGNING_SECRET;export async function trackSignup({ email, external_user_id }) {
const nonce = crypto.randomBytes(16).toString('hex'); // 32 hex minuscules
const ts = Math.floor(Date.now() / 1000).toString();
const body = JSON.stringify({ email, external_user_id, source: 'web', nonce });
const hash = crypto.createHash('sha256').update(body).digest('hex');
const sig = crypto.createHmac('sha256', SECRET)
.update(`${KEY_ID}\n${nonce}\n${ts}\n${hash}`).digest('hex'); await fetch('https://api.dzbuild.app/v1/signups', {
method: 'POST',
headers: {
'Authorization': `DZ-Public ${KEY_ID}`,
'X-DZ-Timestamp': ts,
'X-DZ-Nonce': nonce,
'X-DZ-Signature': sig,
'Idempotency-Key': nonce,
'Content-Type': 'application/json'
},
body
});
}
Le secret de signature ne quitte jamais votre back-end. L'id de clé publique peut être inspecté par n'importe qui — c'est voulu. Voir Inscriptions pour le contrat complet.
Tunneling local pour les webhooks
Les webhooks DZBuild ont besoin d'une URL HTTPS publique. Pour tester localement :
# ngrok ngrok http 3000# Cloudflare Tunnel cloudflared tunnel --url http://localhost:3000# tailscale serve tailscale serve https / http://localhost:3000
Enregistrez l'URL du tunnel comme cible webhook. Mettez à jour à chaque restart (ngrok free change d'URL ; payez 8$/mois pour un sous-domaine stable).
Politique de rotation
Faites tourner les clés :
Trimestriellement pour les clés prod (mettez un rappel calendrier)
Immédiatement si une clé peut avoir fuité (committée à git, screenshot, envoyée en chat)
Au départ d'un membre d'équipe s'il avait accès au coffre
Procédure :
Créez une nouvelle clé avec
POST /v1/keys(voir Comment obtenir une clé, concrètement). Elle hérite du tier et du flag pilote de l'ancienne.Mettez à jour votre coffre / env vars vers la nouvelle clé.
Déployez. Vérifiez le trafic via
GET /v1/usage.Après 24 h de trafic propre sur la nouvelle, révoquez l'ancienne :
DELETE /v1/keys/{old_key_id}.
Erreurs courantes
Erreur | Conséquence | Fix |
| La clé est dans l'historique git pour toujours ; rotation immédiate |
|
Clé plateforme dans le code navigateur | Les clients voient et volent la clé dans network tab | Move au back-end / serverless ; rotation |
| Pareil | env vars ; rotation |
Même clé pour dev et prod | Une boulette dev touche la prod | Deux clés ; ne jamais partager |
Secret webhook perdu | Plus moyen de vérifier les livraisons | Supprimez le webhook, enregistrez-en un autre |
Logguer la clé API dans les logs app | Auditeurs / log shippers / quiconque a accès aux logs voit | Redactez les secrets dans le logger ; rotation |
Checklist avant prod
[ ]
.envest dans.gitignore(et n'a jamais été committé par accident)[ ] Clé prod nommée
prodet utilisée seulement en prod[ ] Clé dev nommée
devet seulement en dev/staging[ ] Secrets webhooks dans secret manager, pas dans le code
[ ] En-tête
Authorizationsur chaque appel API depuis le back-end[ ] Le code navigateur ne voit jamais
dzpk_live_*(seulementdzpub_live_*si flow clé publique)[ ] Votre endpoint webhook est sur une URL indevinable et relit les enregistrements via l'API avant d'agir (les signatures API v1 ne sont pas encore vérifiables)
[ ] Rappel rotation de clés sur votre calendrier
[ ] Logging qui ne capture PAS les bodies complets (peuvent contenir des clés API en client headers)