API BizSign pour intégrateurs
Utilisez l'API native BizSign ou les couches de compatibilité DocuSign et YouSign avec les mêmes identifiants OAuth2.
1. Créer une application
Un administrateur d'organisation crée et gère ses identifiants depuis API & Développeurs. Le secret n'est affiché qu'une fois : copiez-le immédiatement dans votre coffre de secrets.
Vous n'avez pas encore de compte ou l'accès API n'est pas disponible selon votre offre ? Le formulaire pour les prospects permet de décrire votre projet à l'équipe BizSign.
Tester une première demande
URL de cet environnement : https://bizsign.fr.
Le titulaire crée son compte, termine son enrôlement, puis reçoit l’offre de test sur son organisation.
Cette offre doit autoriser l’accès API, la création d’applications, les scopes nécessaires,
le niveau de signature et les volumes utilisés. La gratuité seule n’ouvre pas ces droits.
L’exemple utilise la compatibilité YouSign et un PDF de test.
Renseignez les variables indiquées au début du script, puis exécutez-le dans votre terminal.
Utilisez une adresse et un mobile de test sous votre contrôle. Le niveau
electronic_signature correspond à la Signature Vérifiée de BizSign ;
l’offre doit l’autoriser et prévoir des actes pour ce niveau. L’activation consomme les unités selon votre offre.
L’activation YouSign ne déclenche actuellement pas d’email d’invitation ;
le script récupère le lien de signature pour l’ouvrir dans votre plateforme.
Le secret reste côté serveur ; ne l’intégrez pas au JavaScript du navigateur.
#!/usr/bin/env bash
# Exemple de test : crée et active une demande réelle, avec un destinataire de test.
# Prérequis : bash, curl, jq, base64 ; un PDF et une application autorisée par l'offre.
set -euo pipefail
: "${BASE_URL:?Renseigner l’URL BizSign de votre environnement}"
: "${CLIENT_ID:?Renseigner le Client ID}"
: "${CLIENT_SECRET:?Renseigner le secret depuis votre gestionnaire de secrets}"
: "${TEST_SIGNER_EMAIL:?Renseigner une adresse de test sous votre contrôle}"
: "${TEST_SIGNER_PHONE:?Renseigner le mobile de test au format international}"
: "${PDF_PATH:?Renseigner le chemin d’un PDF de test}"
BASE_URL="${BASE_URL%/}"
export CLIENT_ID CLIENT_SECRET TEST_SIGNER_EMAIL TEST_SIGNER_PHONE
# Le secret passe dans le corps, sans interpolation dans une commande shell.
TOKEN=$(jq -nr '
"grant_type=client_credentials&client_id=" + (env.CLIENT_ID|@uri)
+ "&client_secret=" + (env.CLIENT_SECRET|@uri)
+ "&scope=" + ("signature:read signature:write documents:read documents:write"|@uri)
' | curl --fail-with-body --silent --show-error \
-H 'Content-Type: application/x-www-form-urlencoded' --data-binary @- \
"$BASE_URL/api/compat/oauth/token" | jq -er '.access_token')
unset CLIENT_SECRET
api() {
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' "$@"
}
API_BASE="$BASE_URL/api/compat/yousign/v3"
REQUEST_ID=$(api -X POST "$API_BASE/signature_requests" \
--data '{"name":"Test intégration BizSign","delivery_mode":"none"}' | jq -er '.id')
DOCUMENT_ID=$(base64 < "$PDF_PATH" | tr -d '\n' \
| jq -Rs '{file:.,file_name:"test.pdf",nature:"signable_document"}' \
| api -X POST "$API_BASE/signature_requests/$REQUEST_ID/documents" \
--data-binary @- | jq -er '.id')
export DOCUMENT_ID
SIGNER_ID=$(jq -n '{
info:{first_name:"Test",last_name:"Intégration",email:env.TEST_SIGNER_EMAIL,phone_number:env.TEST_SIGNER_PHONE},
signature_level:"electronic_signature",
signature_authentication_mode:"otp_sms",
fields:[{document_id:env.DOCUMENT_ID,type:"signature",page:1,x:50,y:100,width:180,height:60}]
}' | api -X POST "$API_BASE/signature_requests/$REQUEST_ID/signers" \
--data-binary @- | jq -er '.id')
# L’activation réserve les unités selon votre offre. Ce n’est pas une simulation.
api -X POST "$API_BASE/signature_requests/$REQUEST_ID/activate" > /dev/null
# Ouvrir signature_link dans un navigateur pour signer le PDF de test.
api "$API_BASE/signature_requests/$REQUEST_ID/signers/$SIGNER_ID" | jq '{status,signature_link}'
# Après signature : consulter l’état, puis télécharger le PDF.
api "$API_BASE/signature_requests/$REQUEST_ID" | jq '{id,status}'
# Quand status vaut done :
# api "$API_BASE/signature_requests/$REQUEST_ID/documents/$DOCUMENT_ID/download" -o signe.pdf
2. Authentification OAuth2
L'API utilise le flux OAuth2 client_credentials.
Le scope accordé est l'intersection entre la demande, les droits de l'application
et ceux disponibles selon votre offre.
curl -X POST https://bizsign.fr/api/compat/oauth/token \
-d "grant_type=client_credentials" \
-d "client_id=VOTRE_CLIENT_ID" \
-d "client_secret=VOTRE_CLIENT_SECRET" \
-d "scope=signature:read signature:write"
Envoyez ensuite Authorization: Bearer <access_token>
sur chaque appel natif ou compatible. Une demande sans scope disponible renvoie
invalid_scope.
3. Scopes
| Scope | Autorise |
|---|---|
signature:read | Lire les demandes de signature et les enveloppes |
signature:write | Créer, envoyer et annuler des demandes de signature |
documents:read | Lire et télécharger les documents |
documents:write | Ajouter et remplacer des documents |
templates:read | Lire les modèles |
templates:write | Créer et modifier les modèles |
contacts:read | Lire les contacts |
webhooks:read | Lire les souscriptions webhooks |
webhooks:write | Créer et supprimer des souscriptions webhooks |
usage:read | Consulter l'utilisation de l'API |
Les anciens scopes de compatibilité restent acceptés :
envelopes:read → signature:read,
envelopes:write → signature:write,
signature_requests:read → signature:read,
signature_requests:write → signature:write
.
4. Limites et quotas
- Le débit est plafonné selon votre offre, par organisation et par application.
- Les réponses exposent
X-RateLimit-Limit,X-RateLimit-RemainingetX-RateLimit-Reset. - Le quota mensuel est partagé par les applications de l'organisation ; ses en-têtes sont
X-Quota-Limit,X-Quota-UsedetX-Quota-Reset. - Un refus temporaire fournit
Retry-After. Selon votre offre, le dépassement mensuel est bloqué ou facturé.
Consultez les compteurs et l'historique dans Consommation.
5. Erreurs du péage API
| Statut | Code | Réponse |
|---|---|---|
| 403 | insufficient_scope | {"error":"insufficient_scope","required":[…]} et WWW-Authenticate |
| 403 | api_access_requires_business_plan | {"detail":"api_access_requires_business_plan"} |
| 429 | rate_limited | Inclut retry_after et les en-têtes de débit. |
| 429 | quota_exceeded | Inclut quota, used, resets_at et les en-têtes de quota. |
6. Révocation
Révoquez un jeton devenu inutile ou compromis. Après authentification du client, la réponse est idempotente, même si le jeton est déjà inconnu ou révoqué.
curl -X POST https://bizsign.fr/api/compat/oauth/revoke \
-d "token=VOTRE_ACCESS_TOKEN" \
-d "token_type_hint=access_token" \
-d "client_id=VOTRE_CLIENT_ID" \
-d "client_secret=VOTRE_CLIENT_SECRET"
Renouveler le secret dans API & Développeurs invalide immédiatement tous les tokens déjà émis pour l'application.
7. Référence des opérations
API native
Workflows, documents, modèles, contacts et vérification d'horodatage sous /api/v1.
Compat DocuSign
Base /api/compat/docusign/v2.1/accounts/{account_id}.
Compat YouSign
Base /api/compat/yousign/v3.
Schéma OpenAPI public
Le document JSON ne contient que les routes destinées aux intégrateurs et indique x-required-scopes pour chaque opération protégée.
Compatibilité et refus à l’envoi
La couverture DocuSign et YouSign est partielle. Les tableaux ci-dessous sont publiés depuis la matrice versionnée du produit. Un endpoint YouSign absent de cette liste retourne 404. Une opération indiquée 501 n’est pas implémentée. Les refus de contrat ne consomment aucune unité ; corrigez le droit ou le quota avant de réessayer.
Authentification et droits du forfait
| Cause | Statut | YouSign type | DocuSign errorCode |
|---|---|---|---|
| Niveau non autorisé par le contrat | 403 | forbidden | PLAN_LIMIT_REACHED |
| Quota mensuel d'actes atteint pour ce niveau | 403 | forbidden | SIGNATURE_ACT_QUOTA_REACHED |
| Quota mensuel de Biz atteint | 400 | bad_request | QUOTA_EXCEEDED |
| Facture échue, nouvelles demandes suspendues | 402 | payment_required | PAYMENT_REQUIRED |
| Engagement de volume épuisé | 402 | payment_required | PAYMENT_REQUIRED |
DocuSign eSignature REST v2.1
| Endpoint | Méthode | Statut | Notes |
|---|---|---|---|
| /envelopes | GET | Supporté | Filtres statut / dates / texte, pagination |
| /envelopes | POST | Supporté | Documents, recipients et tabs fournis à la création |
| /envelopes/{id} | GET | Supporté | |
| /envelopes/{id} | PUT | Supporté | Envoi (sent) et annulation (voided) |
| /envelopes/{id}/documents | GET | Supporté | Inclut le document virtuel combined |
| /envelopes/{id}/documents | PUT | Non implémenté (501) | Fournir tous les documents au POST /envelopes |
| /envelopes/{id}/documents/{docId} | GET | Supporté | Téléchargement PDF |
| /envelopes/{id}/recipients | GET | Supporté | |
| /envelopes/{id}/recipients | POST | Non implémenté (501) | Fournir tous les recipients au POST /envelopes |
| /envelopes/{id}/recipients | PUT | Non implémenté (501) | |
| /envelopes/{id}/recipients/{rid}/tabs | GET | Supporté | |
| /envelopes/{id}/recipients/{rid}/tabs | POST | Non implémenté (501) | Fournir les tabs au POST /envelopes |
| /envelopes/{id}/recipients/{rid}/tabs | PUT | Non implémenté (501) | |
| /envelopes/{id}/recipients/{rid}/tabs | DELETE | Non implémenté (501) | |
| /envelopes/{id}/views/recipient | POST | Supporté (partiel) | Retourne une URL tokenisée réelle vers la cérémonie de signature BizSign (/sign/{token}). Limite : pas de redirection vers returnUrl en fin de cérémonie, et authenticationMethod n'est pas appliqué. |
YouSign API v3
| Endpoint | Méthode | Statut | Notes |
|---|---|---|---|
| /signature_requests | POST | Supporté | Création en statut draft |
| /signature_requests/{id} | GET | Supporté | |
| /signature_requests/{id}/activate | POST | Supporté | |
| /signature_requests/{id}/cancel | POST | Supporté | |
| /signature_requests/{id}/documents | GET | Supporté | |
| /signature_requests/{id}/documents | POST | Supporté | Contenu base64 |
| /signature_requests/{id}/documents/{docId} | DELETE | Supporté | Suppression réelle (document + champs + fichier), requêtes draft uniquement |
| /signature_requests/{id}/documents/{docId}/download | GET | Supporté | PDF final de la pièce après finalisation ; 409 en cours, 404 si le fichier manque |
| /signature_requests/{id}/documents/{docId}/fields | GET | Supporté | |
| /signature_requests/{id}/signers | POST | Supporté | Création d'utilisateur invité si nécessaire |
| /signature_requests/{id}/signers/{signerId} | GET | Supporté | |
| /signature_requests/{id}/signers/{signerId} | DELETE | Supporté | Suppression réelle (signataire + champs assignés), requêtes draft uniquement |
| /webhooks | POST / GET | Supporté | Abonnements globaux par client API |
| /webhooks/{id} | DELETE | Supporté | Désactivation (soft delete) |
Recette du parcours client : états, fichiers, appels et reprise (#330)
| État ou événement | Fichiers disponibles | Qui | Appel | Reprise |
|---|---|---|---|---|
| Brouillon créé, documents et participants ajoutés | Originaux seulement | Application, scopes signature:write, documents:write | POST …/signature_requests, …/documents, …/signers | Rejouer l’appel en échec ; rien n’est réservé avant l’envoi |
| Envoi explicite (activate ou status: sent) | Originaux | Application, signature:write | POST …/signature_requests/{id}/activate | Un refus 403/402 (PLAN_LIMIT_REACHED, SIGNATURE_ACT_QUOTA_REACHED, CONSENT_REQUIRED, impayé) ne consomme rien : corriger la cause, rejouer |
| signature_request.activated reçu | Originaux ; signature_link du participant en attente | Application, signature:read | GET …/signers/{signer_id} | Notification manquée : relire le détail de la demande |
| signer.done reçu, status encore ongoing | Originaux ; PDF de preuve provisoire | Application, signature:read + documents:read | GET /api/v1/workflows/{id}/proof/status, …/proof?format=pdf | Attendre les autres participants |
| status=done, finalisation en cours | PDF de preuve ; PDF finaux pas encore | Application, documents:read | GET …/documents/{document_id}/download → 409 DOCUMENT_NOT_READY | Attendre Retry-After secondes, rejouer le téléchargement |
| signature_request.done reçu (jalon EMAILS) | Un PDF final par document ; PDF de preuve définitif | Application, documents:read | GET …/documents/{document_id}/download, un appel par document | Notification manquée ou perdue : le téléchargement se relit à tout moment ; répétée : dédupliquer sur l’identifiant d’événement |
| Finalisation en échec | PDF de preuve ; finaux indisponibles | Application | Même appel → 409 DOCUMENT_FINALIZATION_FAILED | Reprise côté BizSign (support) ; ne jamais substituer un original |
| Ancrage soumis, non confirmé (ots_state=initial) | OTS initial, archive ZIP (PDF, OTS, ancrage.json, README.txt, jetons .tst) | Application, signature:read + documents:read | GET …/proof?format=ots, …/proof?format=zip | 409 OTS_NOT_AVAILABLE tant que l’ancrage n’a pas été soumis : relire proof/status |
| signature_request.bitcoin_confirmed reçu (bitcoin_confirmed=true) | OTS complété, archive à jour | Application | Retélécharger ots et zip ; ots_sha256 a changé | Notification manquée : relire proof/status ; aucune renotification rétroactive |
| Archive incomplète | PDF et OTS séparés | Application | …/proof?format=zip → 409 PROOF_ARCHIVE_INCOMPLETE | Conserver PDF et OTS ; signaler la demande au support |
| Demande d’une autre organisation, document inconnu | Aucun | — | Toute route → 404 | Vérifier l’organisation de l’application ; jamais de repli |
Recette complète du parcours client
Ce second script enchaîne tout le parcours que votre plateforme devra tenir : jeton, abonnement aux notifications, demande à deux documents distincts, participant, envoi explicite, signature dans le parcours réel, attente de la fin, téléchargement de chaque PDF final avec traitement des réponses temporaires, état et exports de preuve, observation distincte de la confirmation Bitcoin, et reprise après une notification manquée ou répétée. Il consigne un journal sans secret ni donnée personnelle, avec la version testée.
Trois états à ne jamais confondre : status=done (toutes les
signatures sont posées), PDF final disponible (finalisation, scellement et horodatage terminés, annoncés
par signature_request.done) et bitcoin_confirmed=true (ancrage vérifié, annoncé par
signature_request.bitcoin_confirmed, sans délai garanti). Le tableau
Recette du parcours client de la rubrique compatibilité donne, pour chaque état, les fichiers
disponibles, qui peut les récupérer, l’appel à faire et la reprise.
#!/usr/bin/env bash
# Recette du parcours client BizSign par l’API (#330) : jeton, demande à deux
# documents, participant, envoi explicite, signature dans le parcours réel,
# notifications, PDF finaux, preuves, confirmation Bitcoin, reprise.
# Prérequis : bash, curl, jq, base64, sha256sum ; deux PDF de test sans donnée
# personnelle ; une application autorisée par l’offre (scopes ci-dessous).
# Rien n’est simulé : l’envoi réserve les unités selon votre offre.
set -euo pipefail
: "${BASE_URL:?Renseigner l’URL BizSign de votre environnement}"
: "${CLIENT_ID:?Renseigner le Client ID}"
: "${CLIENT_SECRET:?Renseigner le secret depuis votre gestionnaire de secrets}"
: "${TEST_SIGNER_EMAIL:?Renseigner une adresse de test sous votre contrôle}"
: "${TEST_SIGNER_PHONE:?Renseigner le mobile de test au format international}"
: "${PDF_PATH:?Renseigner le chemin du premier PDF de test}"
: "${PDF2_PATH:?Renseigner le chemin du second PDF de test}"
WEBHOOK_URL="${WEBHOOK_URL:-}" # facultatif : URL HTTPS de votre récepteur
POLL_SECONDS="${POLL_SECONDS:-30}" # intervalle de relance de l’état
OUT_DIR="${OUT_DIR:-recette-$(date -u +%Y%m%dT%H%M%SZ)}"
BASE_URL="${BASE_URL%/}"
mkdir -p "$OUT_DIR"
export CLIENT_ID CLIENT_SECRET TEST_SIGNER_EMAIL TEST_SIGNER_PHONE
log() { printf '%s %s\n' "$(date -u +%H:%M:%S)" "$*" >&2; }
note() { # consigne une observation, jamais un secret ni une donnée personnelle
jq -n --arg step "$1" --arg value "$2" --arg at "$(date -u +%FT%TZ)" \
'{at:$at,step:$step,value:$value}' >> "$OUT_DIR/journal.jsonl"
}
# 1. Jeton — le secret passe dans le corps, jamais dans une ligne de commande.
TOKEN=$(jq -nr '
"grant_type=client_credentials&client_id=" + (env.CLIENT_ID|@uri)
+ "&client_secret=" + (env.CLIENT_SECRET|@uri)
+ "&scope=" + ("signature:read signature:write documents:read documents:write webhooks:read webhooks:write"|@uri)
' | curl --fail-with-body --silent --show-error \
-H 'Content-Type: application/x-www-form-urlencoded' --data-binary @- \
"$BASE_URL/api/compat/oauth/token" | jq -er '.access_token')
unset CLIENT_SECRET
note "token" "obtenu"
api() { curl --silent --show-error -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' "$@"; }
must() { api --fail-with-body "$@"; }
YS="$BASE_URL/api/compat/yousign/v3"
# Version testée : le contrat OpenAPI public porte la version du produit.
VERSION=$(curl --silent --fail "$BASE_URL/api/openapi.json" | jq -r '.info.version // "inconnue"')
note "version" "$VERSION"
# 2. Notifications — les trois événements qui rythment le parcours. Extension
# BizSign : signature_request.bitcoin_confirmed n’existe pas chez YouSign.
if [ -n "$WEBHOOK_URL" ]; then
export WEBHOOK_URL
WEBHOOK_ID=$(jq -n '{url:env.WEBHOOK_URL,
events:["signature_request.activated","signature_request.done","signature_request.bitcoin_confirmed"]}' \
| must -X POST "$YS/webhooks" --data-binary @- | jq -er '.id')
note "webhook" "abonnement $WEBHOOK_ID"
fi
# 3. Demande — deux documents DISTINCTS, un participant avec un champ sur chacun.
REQUEST_ID=$(must -X POST "$YS/signature_requests" \
--data '{"name":"Recette BizSign — deux documents","delivery_mode":"none"}' | jq -er '.id')
note "request" "$REQUEST_ID"
add_document() { # $1 chemin, $2 nom publié
base64 < "$1" | tr -d '\n' \
| jq -Rs --arg name "$2" '{file:.,file_name:$name,nature:"signable_document"}' \
| must -X POST "$YS/signature_requests/$REQUEST_ID/documents" --data-binary @- | jq -er '.id'
}
DOC1=$(add_document "$PDF_PATH" "recette-1.pdf")
DOC2=$(add_document "$PDF2_PATH" "recette-2.pdf")
export DOC1 DOC2
SIGNER_ID=$(jq -n '{
info:{first_name:"Test",last_name:"Recette",email:env.TEST_SIGNER_EMAIL,phone_number:env.TEST_SIGNER_PHONE},
signature_level:"electronic_signature",
signature_authentication_mode:"otp_sms",
fields:[{document_id:env.DOC1,type:"signature",page:1,x:50,y:100,width:180,height:60},
{document_id:env.DOC2,type:"signature",page:1,x:50,y:100,width:180,height:60}]
}' | must -X POST "$YS/signature_requests/$REQUEST_ID/signers" --data-binary @- | jq -er '.id')
# 4. Envoi EXPLICITE — c’est l’activation qui engage et réserve les unités.
# Un refus de contrat (403 PLAN_LIMIT_REACHED, SIGNATURE_ACT_QUOTA_REACHED,
# CONSENT_REQUIRED…) sort ici, lisible, et ne consomme rien.
must -X POST "$YS/signature_requests/$REQUEST_ID/activate" > /dev/null
note "activate" "envoyée"
# 5. Signature dans le parcours réel : ouvrir le lien dans un navigateur, sur
# le mobile de test, et signer les deux documents (code SMS).
must "$YS/signature_requests/$REQUEST_ID/signers/$SIGNER_ID" \
| jq -r '"Lien de signature : \(.signature_link)\nExpire le : \(.signature_link_expiration_date)"' >&2
# 6. Attendre la fin : ici par relance de l’état ; en production, par le
# webhook signature_request.done. Les deux mènent au même téléchargement.
# Signature terminée (status=done) ≠ PDF disponible ≠ Bitcoin confirmé.
until [ "$(must "$YS/signature_requests/$REQUEST_ID" | jq -r '.status')" = "done" ]; do
log "en attente de la signature… (relance dans ${POLL_SECONDS}s)"; sleep "$POLL_SECONDS"
done
note "status" "done"
# 7. Chaque PDF final, par identifiant. 409 DOCUMENT_NOT_READY + Retry-After :
# la finalisation est en cours, on attend le délai annoncé puis on rejoue.
# 409 DOCUMENT_FINALIZATION_FAILED : une reprise côté BizSign est nécessaire.
download_final() { # $1 id document, $2 fichier de sortie
local code headers="$OUT_DIR/headers.tmp"
while :; do
code=$(api -D "$headers" -o "$2" -w '%{http_code}' \
"$YS/signature_requests/$REQUEST_ID/documents/$1/download")
case "$code" in
200) sha256sum "$2" | cut -d' ' -f1; return 0 ;;
409) if jq -e '.code=="DOCUMENT_NOT_READY"' "$2" > /dev/null 2>&1; then
local wait; wait=$(grep -i '^Retry-After:' "$headers" | tr -dc '0-9'); wait="${wait:-$POLL_SECONDS}"
log "document $1 : finalisation en cours, nouvel essai dans ${wait}s"; sleep "$wait"
else
log "document $1 : $(jq -r '.code // .detail' "$2") — reprise nécessaire, voir le support"; return 1
fi ;;
*) log "document $1 : HTTP $code"; cat "$2" >&2; return 1 ;;
esac
done
}
note "pdf1_sha256" "$(download_final "$DOC1" "$OUT_DIR/recette-1-signe.pdf")"
note "pdf2_sha256" "$(download_final "$DOC2" "$OUT_DIR/recette-2-signe.pdf")"
# 8. Preuves — état d’abord, puis exports. Le PDF de preuve existe dès la fin ;
# l’OTS et l’archive attendent le premier ancrage ; bitcoin_confirmed est
# une observation DISTINCTE, souvent plus tardive (aucun délai garanti).
V1="$BASE_URL/api/v1/workflows/$REQUEST_ID"
STATUS=$(must "$V1/proof/status")
echo "$STATUS" | jq '{documents_state,proof_state,ots_state,public_anchor_status,bitcoin_confirmed}' >&2
note "proof_status" "$(echo "$STATUS" | jq -c '{documents_state,proof_state,ots_state,bitcoin_confirmed}')"
must "$V1/proof?format=pdf" -o "$OUT_DIR/preuve.pdf"
for fmt in ots zip; do
code=$(api -o "$OUT_DIR/preuve.$fmt" -w '%{http_code}' "$V1/proof?format=$fmt")
[ "$code" = 200 ] && note "proof_$fmt" "téléchargé" || note "proof_$fmt" "HTTP $code $(jq -r '.detail' "$OUT_DIR/preuve.$fmt" 2>/dev/null)"
done
# 9. Reprise. Notification manquée : l’état ci-dessus se relit à tout moment,
# aucun événement n’est nécessaire pour récupérer les fichiers. Notification
# reçue deux fois : dédupliquer sur l’identifiant d’événement. Confirmation
# Bitcoin attendue : relire proof/status jusqu’à bitcoin_confirmed=true,
# puis retélécharger l’OTS et l’archive (l’empreinte ots_sha256 change).
if [ "$(echo "$STATUS" | jq -r '.bitcoin_confirmed')" != "true" ]; then
log "Bitcoin non confirmé à cet instant : relire $V1/proof/status plus tard, puis reprendre l’OTS et l’archive."
fi
[ -n "${WEBHOOK_ID:-}" ] && must -X DELETE "$YS/webhooks/$WEBHOOK_ID" > /dev/null
rm -f "$OUT_DIR/headers.tmp"
log "Recette terminée. Journal sans secret : $OUT_DIR/journal.jsonl"
Récupérer les documents
Téléchargez chaque document par son identifiant. Après finalisation, chaque pièce principale fournit son propre PDF final scellé et horodaté. Les annexes restent leurs fichiers d’origine. La confirmation Bitcoin n’est pas nécessaire pour récupérer les PDF.
- YouSign :
GET /api/compat/yousign/v3/signature_requests/{id}/documents/{document_id}/download. - DocuSign :
GET /api/compat/docusign/v2.1/accounts/{account_id}/envelopes/{id}/documents/{document_id}. 409 DOCUMENT_NOT_READY: la finalisation est en cours. Respectez l’en-têteRetry-Afteravant de retenter le téléchargement.409 DOCUMENT_FINALIZATION_FAILED: la finalisation a échoué. Une reprise est nécessaire ; contactez le support si cet état persiste.404: demande inaccessible, document introuvable ou fichier indisponible. Un original ou une autre pièce ne remplace jamais un PDF final manquant.
Le code est dans code pour YouSign et
errorCode pour DocuSign. Avant la fin des signatures, hors finalisation en cours,
le téléchargement retourne l’original. Le statut « terminé » peut précéder la finalisation :
traitez la réponse du téléchargement. Le webhook de fin est enregistré après
la préparation des PDF, leur scellement, leur horodatage et la preuve privée.
Les fichiers peuvent alors être récupérés, même si un email de fin doit être repris.
Il n’attend pas Bitcoin.
Récupérer les preuves
Ces routes natives BizSign utilisent le même jeton applicatif,
y compris pour une demande créée avec YouSign ou DocuSign. Accordez les scopes
signature:read et documents:read. L’application accède au dossier
global des demandes de son organisation ; une session de signataire n’y donne pas accès.
GET /api/v1/workflows/{id}/proof/status: disponibilité et liens de téléchargement.GET /api/v1/workflows/{id}/proof: dossier PDF, disponible dès une première signature ou un premier refus.GET /api/v1/workflows/{id}/proof?format=ots: fichier OpenTimestamps le plus récent.GET /api/v1/workflows/{id}/proof?format=zip: PDF, fichier OTS, jetons d’horodatage RFC 3161, références d’ancrage et notice README.txt. Conservez séparément les documents signés.
proof_state vaut unavailable,
provisional ou definitive. Le PDF provisoire porte un filigrane.
Le dossier devient définitif après les étapes cryptographiques, même si un email est en reprise.
documents_state indique pending, ready ou failed.
ots_state vaut unavailable, initial
ou completed. Une preuve initiale n’est pas une confirmation Bitcoin.
Consultez bitcoin_confirmed et public_anchor_status.
Quand ots_sha256 change, téléchargez de nouveau l’OTS et l’archive avec les liens
de downloads. Ces liens nécessitent toujours votre jeton.
Un export indisponible répond 409 avec
detail: PROOF_NOT_AVAILABLE avant la première participation ou
detail: OTS_NOT_AVAILABLE pour l’OTS et l’archive encore absents.
Un ZIP dont un jeton d’horodatage manque ou ne correspond pas à l’ancrage répond
detail: PROOF_ARCHIVE_INCOMPLETE (409) ; les exports PDF et OTS séparés restent accessibles.
Le PDF reste téléchargeable sans OTS. Une demande inaccessible répond 404.
Abonnez-vous aussi à la confirmation Bitcoin décrite ci-dessous pour récupérer la preuve complétée.
Le ZIP conserve les jetons d’origine dans horodatages/*.tst
(DER, CMS TimeStampToken). Le tableau timestamp_tokens de ancrage.json
associe chaque fichier à son document et aux empreintes SHA-256 du PDF final et du jeton.
README.txt explique la contre-vérification avec OpenSSL ; la confiance dans les certificats
de l’autorité TSA doit être établie indépendamment. Une archive ancienne sans manifeste d’horodatage
exploitable répond aussi PROOF_ARCHIVE_INCOMPLETE.
8. Webhooks
Les souscriptions compatibles YouSign sont gérées sous
/api/compat/yousign/v3/webhooks avec les scopes
webhooks:read et webhooks:write.
Lorsqu'un secret est configuré, BizSign signe le corps exact avec HMAC-SHA256 dans
X-Yousign-Signature-256. La signature HMAC des notifications
compatibles DocuSign n'est pas encore disponible.
La confirmation Bitcoin dispose de deux extensions propres à BizSign :
signature_request.bitcoin_confirmed pour une souscription YouSign
(ajoutez ce nom à events) et
envelope-bitcoin-confirmed pour DocuSign
(ajoutez envelopeEventStatusCode: bitcoin-confirmed à
eventNotification.envelopeEvents lors de la création).
Ces noms ne sont pas des événements natifs des fournisseurs.
Cet événement est enregistré uniquement après vérification de l’attestation OTS et du bloc Bitcoin.
Le bloc proof se trouve dans data.signature_request
pour YouSign ou data.envelopeSummary pour DocuSign.
Il fournit bitcoin_confirmed: true, ots_state: completed,
ots_sha256, bitcoin_block_height, bitcoin_block_hash,
confirmed_at (UTC), request_id, status_url et
downloads (PDF, OTS, ZIP). Les liens sont relatifs à BizSign et nécessitent votre jeton
avec les scopes signature:read et documents:read.
Téléchargez et conservez la nouvelle version de l’OTS et de l’archive ; aucun fichier n’est joint au webhook.
Une souscription créée après la confirmation ne reçoit pas d’événement rétroactif : consultez alors l’état.
Bitcoin ne bloque pas la livraison des PDF finaux et aucun délai de confirmation n’est garanti.
signature_request.done (YouSign) et envelope-completed
(DocuSign) annoncent que les PDF finaux sont prêts. Une reprise des emails de fin
ne bloque ni cette notification ni le téléchargement API. Le worker prend en charge les notifications enregistrées
et leurs reprises chaque minute ; la réception dépend aussi de sa charge et de
la disponibilité de votre serveur. Répondez avec un statut HTTP 2xx après prise en charge.
Une notification peut être rediffusée après une réponse perdue : dédupliquez par
event_id pour YouSign, ou par demande et événement pour DocuSign.