Composizione, firma e flusso in blocco
Componi molti documenti da un template, o manda in firma molte buste, con una sola richiesta. Un batch è accettato per intero o rifiutato per intero, gira in background, e rileggi il risultato di ogni elemento man mano che finisce. Si paga solo ciò che è stato consegnato.
Ogni documento è composto esattamente dal codice che compone una singola
POST /api/v1/compose/documents/generate, così un documento in un batch è lo stesso
documento che avresti avuto uno alla volta, allo stesso prezzo. Un batch di firma è uguale: ogni busta è
creata e inviata dal codice dietro una singola
POST /api/v1/sign/envelopes?send=true, con la stessa validazione, gli
stessi predefiniti e filtri, gli stessi rifiuti e gli stessi codici di errore. Un batch non è mai un modo per
aggirare una regola: quello che una singola chiamata rifiuterebbe, un batch lo rifiuta con lo stesso codice, e
quello che costa una singola chiamata lo costa un elemento.
Le chiamate
| Chiamata | Ambito | Cosa fa |
|---|---|---|
POST /api/v1/bulk/compose | bulk:submit e documents:compose | Invia un batch, in linea o con fileId. Risponde 202 con il job. |
POST /api/v1/bulk/sign | bulk:submit e signatures:ses | Invia un batch di buste da creare e inviare (firma in blocco). Risponde 202 con il job. |
POST /api/v1/bulk/flow | bulk:submit e flow:* | Invia un batch di flussi: ogni elemento compone un documento e lo manda in firma (Document Fly in blocco). Risponde 202 con il job. |
POST /api/v1/bulk/files | bulk:submit | Carica un batch JSONL o CSV, ricevi un fileId. Risponde 201. |
GET /api/v1/bulk/jobs/{id} | bulk:read | Il job: stato, conteggi, crediti e, quando è finito, la riconciliazione. Interroga questo. |
GET /api/v1/bulk/jobs/{id}/items | bulk:read | Il risultato di ogni documento, in ordine, a pagine. |
GET /api/v1/bulk/jobs | bulk:read | I job della tua organizzazione, dal più recente (status, limit fino a 100). |
GET /api/v1/bulk/jobs/{id}/results | bulk:read e documents:download | Il pacchetto dei risultati: uno ZIP con ogni PDF prodotto, un manifesto e un results.csv. |
POST /api/v1/bulk/jobs/{id}/cancel | bulk:submit | Ferma un job. |
POST /api/v1/bulk/jobs/{id}/retry | bulk:submit | Rimette in lavoro gli elementi falliti da ritentare, come nuovi tentativi nello stesso job. |
I documenti si scaricano con GET /api/v1/compose/documents/{id} (ambito documents:download); ogni
elemento porta il suo documentId e downloadUrl. I batch di composizione richiedono che l'interruttore
Composer Fly della tua organizzazione sia acceso e quelli di firma l'interruttore Signer Fly, come le singole chiamate; job
e caricamento sono condivisi, così un cliente Signer Fly senza Composer Fly può firmare in blocco e leggere i propri
job.
Ogni operazione in blocco è anche un acquisto a sé. Oltre al prodotto a cui appartiene (Composer Fly per
/bulk/compose, Signer Fly per /bulk/sign, Document Fly per /bulk/flow), la tua
organizzazione ha bisogno dell'opzione bulk corrispondente. Un'organizzazione che si è registrata da sola le ha tutte e tre
fin dall'inizio, così puoi provare subito il bulk (usa "isTest": true, che è gratis); per un'organizzazione che
abbiamo creato noi, la attiviamo noi. Senza, la chiamata risponde 403 FEATURE_NOT_ENABLED con
details.feature uguale a bulkCompose, bulkSign o bulkFlow; non si riserva e non
si addebita nulla. GET /api/v1/usage elenca quello che la tua organizzazione ha sotto
organisation.entitlements.
Inviare un batch
POST /api/v1/bulk/compose
Authorization: Bearer lpdc_...
Idempotency-Key: month-end-2026-09
{
"settings": { "templateId": "invoice-standard-8bc99b" },
"items": [
{ "externalId": "inv-1001", "data": { "customer": { "name": "Acme Srl" }, "total": "1.240,00" } },
{ "externalId": "inv-1002", "data": { "customer": { "name": "Beta SpA" }, "total": "380,00" } }
],
"options": { "maxItems": 500 }
}
settingsè lo stesso oggetto che prende una singola chiamata e si applica a ogni elemento. Ildatadi ogni elemento è quello che manderesti comedataa una singola chiamata.externalIdè il tuo riferimento per l'elemento. È facoltativo, e unico nel batch. Torna sull'elemento così puoi collegare i risultati ai tuoi record.- Manda o
itemsofileId, mai entrambi e mai nessuno dei due (BULK_INPUT_INVALID). options.maxItemspuò abbassare il limite di elementi per questa richiesta, mai alzarlo. Un batch oltre il limite è rifiutato per intero, mai tagliato.- Le chiavi sconosciute in qualunque punto del corpo fuori dal
datadi un elemento sono rifiutate, come ovunque in questa API.
La risposta è 202 Accepted, con un header Location che punta al job. Le impostazioni tornano
come stabilite, e il job sta in output.bulk:
202 Accepted
Location: /api/v1/bulk/jobs/6d1e...
{
"settings": { "templateId": "invoice-standard-8bc99b", "...": "come stabilito" },
"output": {
"status": "pending",
"bulk": {
"jobId": "6d1e...",
"operation": "compose",
"status": "queued",
"isTest": false,
"templateId": "invoice-standard-8bc99b",
"templateVersion": 3,
"totalItems": 2,
"counts": { "queued": 2, "processing": 0, "succeeded": 0, "failed": 0, "cancelled": 0 },
"creditsReserved": 10,
"creditsConsumed": 0,
"creditsHeld": 10,
"creditsReleased": 0,
"links": { "self": "/api/v1/bulk/jobs/6d1e...", "items": "/api/v1/bulk/jobs/6d1e.../items" },
"...": "createdAt, startedAt, completedAt, deadlineAt, resultsExpireAt, error"
},
"credits": { "used": 0, "left": 1237 },
"warnings": [],
"error": null
}
}
output.bulk è la quarta chiave di output e compare solo nelle risposte sotto
/api/v1/bulk. Le tre sezioni dei prodotti (compose, sign, flow) restano al loro
posto e lì sono null.
Cosa non può chiedere un batch
Un batch è rifiutato per intero con FEATURE_NOT_AVAILABLE_IN_BULK (1010), prima di riservare qualunque cosa,
se le sue settings chiedono un sigillo qualificato (qeSeal), una marca temporale qualificata
(qualifiedTimestamp), una password del documento, un responseFormat diverso da
pdf, o un filename. details.fields li nomina uno per uno. Un sigillo o una marca si
addebitano a cose fatte e potrebbero superare quanto riservato; una password non si può tenere in un job conservato; un
elemento non ha una risposta sua e un nome di file ne nominerebbe molti. Manda quei documenti uno alla volta.
Caricare un batch grande
Oltre il limite in linea, carica gli elementi come file JSONL (un oggetto JSON per riga,
{"externalId": "...", "data": {...}}) — o come CSV — e invia con
fileId:
POST /api/v1/bulk/files
Authorization: Bearer lpdc_...
Idempotency-Key: month-end-2026-09-file
Content-Type: application/x-ndjson
{"externalId":"inv-1001","data":{"customer":{"name":"Acme Srl"}}}
{"externalId":"inv-1002","data":{"customer":{"name":"Beta SpA"}}}
201 Created
{ "output": { "bulk": { "file": {
"fileId": "f41a...", "items": 2, "bytes": 131,
"sha256": "...", "expiresAt": "2026-10-03T09:14:02Z" } }, "...": "..." } }
POST /api/v1/bulk/compose
{ "settings": { "templateId": "invoice-standard-8bc99b" }, "fileId": "f41a..." }
- Manda le righe come corpo grezzo della richiesta. Non si decomprime nulla: un
Content-Encodingè rifiutato. - Tutto il file è validato prima di conservare qualunque cosa, e un JSONL è rifiutato per intero se una riga è sbagliata
(
BULK_INPUT_INVALID, con fino a ventidetails.problems, ognuno con la sualine). Un file oltre il suo limite di byte èJOB_TOO_LARGE(1812). - Un file serve a un job. Un caricamento non usato prima di
expiresAtviene eliminato; usarlo dopo, o due volte, èBULK_FILE_NOT_FOUND(1503).
Input CSV
Anche l'esportazione di un foglio di calcolo è un batch. Caricala sulla stessa POST /api/v1/bulk/files con
Content-Type: text/csv (o ?format=csv) e invia il fileId come per un JSONL. Viene letta
negli stessi identici elementi, quindi tutto ciò che viene dopo il lettore — i limiti, la prenotazione, la
composizione, l'addebito — è uguale.
| Intestazione | Diventa |
|---|---|
externalId | Il riferimento dell'elemento (non un campo del payload). |
customer | data.customer, testo. |
customer.name | data.customer.name: un punto annida. |
lines.0.sku, lines.1.sku | Un elenco: data.lines[0].sku, data.lines[1].sku. Gli indici vanno 0, 1, 2… senza buchi. |
total:number, paid:bool, lines:json | Una cella tipizzata. Senza suffisso una cella è testo. :number sono cifre con un meno facoltativo e il punto per i decimali; :bool è true/false, yes/no, 1/0; :json è un valore JSON. |
- La prima riga è l'intestazione. Una cella vuota è assente (il campo non si manda), mai una stringa vuota, e un elemento di elenco con tutte le celle vuote viene scartato.
- Separatore: virgola, punto e virgola, tab o barra verticale, letto dalla riga di intestazione, o
indicato con
?delimiter=comma|semicolon|tab|pipe. Si legge una volta, al caricamento, e si conserva con il file. - Codifica: UTF-8 (un byte-order mark va bene), o
?encoding=iso-8859-1/windows-1252— quello che un Excel italiano scrive ancora. Non si indovina mai: un file non valido nella sua codifica è rifiutato indicando il byte su cui si è fermato. - Un file sbagliato è rifiutato per intero con
BULK_INPUT_INVALIDe non si conserva nulla: vuoto, byte NUL, un'intestazione che non si legge (una colonna doppia o senza nome, un suffisso di tipo sconosciuto, un elenco con un buco), una virgoletta malformata, più righe del limite del file, o nessuna riga utilizzabile. - Una riga sbagliata non fa rifiutare le altre. Diventa un elemento fallito con
error.codeBULK_INPUT_INVALID(1107) e la suaroweline: una cella che non è il numero che dice l'intestazione, il numero sbagliato di celle, unexternalIddoppio o non valido, una riga oltre la dimensione per elemento. Non è mai riservata né addebitata, e non è da ritentare (correggi la riga e mandala in un nuovo batch). - Formule: quello che mandi non viene mai valutato né modificato; una cella che inizia con
=,+,-o@resta testo, perché finisce in un PDF. Ogni CSV che scriviamo noi (results.csv) antepone un apostrofo a quel testo così un foglio di calcolo non può eseguirlo. - Le colonne sono limitate (200, annidate al massimo a 6 livelli, indice di elenco al massimo 99) e non si decomprime nulla.
Firma in blocco
POST /api/v1/bulk/sign invia una busta per elemento. È POST /bulk/compose con un elemento diverso,
quindi tutto quello che c'è in questa pagina — il 202 e il job, l'ammissione per intero o per niente, una
prenotazione per elemento, il worker, gli stati, Idempotency-Key, i limiti, il CSV, il pacchetto, il nuovo
tentativo e il webhook — vale così com'è. Un elemento è creato e inviato dallo stesso servizio che usa una singola
POST /sign/envelopes?send=true.
POST /api/v1/bulk/sign
Authorization: Bearer lpdc_...
Idempotency-Key: nda-2026-q4
{
"settings": {
"documents": [ { "fileId": "9c2e..." } ],
"subject": "NDA 2026"
},
"items": [
{ "externalId": "C-001",
"signers": { "A": { "email": "anna@example.com", "fullName": "Anna Rossi" } } },
{ "externalId": "C-002", "subject": "NDA 2026 - condizioni speciali",
"documents": [ { "fileId": "b17a..." } ],
"signers": { "A": { "email": "bruno@example.com", "fullName": "Bruno Neri" } } }
],
"callbackUrl": { "onSuccess": "https://example.com/hooks/bulk" }
}
settingsè la parte condivisa dellesettingsdi una singola busta (isTest,level,language,subject,documents, …). Idocuments/documentId/subject/messagedi un elemento vi si sovrappongono, e il risultato è validato come la richiesta stessa che prende una singola chiamata. Lo stesso contratto per ogni firmatario è quindisettings.documentsuna volta esignersper elemento.- Le sorgenti sono un
documentIdcomposto o un PDF che hai caricato una volta (POST /api/v1/sign/files, poifileId). UnfileBase64in linea è rifiutato in un batch: caricalo una volta. - Funziona anche un CSV:
externalId,signers.A.email,signers.A.fullNamecon le impostazioni condivise nella richiesta. Una riga con un indirizzo non valido è un elemento fallito, non addebitato. - Si riserva una firma per elemento all'ammissione e la si addebita quando quella busta viene inviata; un elemento che
fallisce è rimborsato. Un batch
isTestspende un elemento di prova per busta e mai credito. settings.callbackUrlè la callback di ogni busta; ilcallbackUrlal primo livello è il webhook di completamento del batch.- Il token deve avere
bulk:submitesignatures:ses: un batch non aggira mai un ambito.
Rifiutato per intero, con i codici di una singola chiamata: FEATURE_NOT_AVAILABLE_IN_TEST_MODE (1009) per una
funzione a pagamento con isTest; SIGNATURE_LEVEL_UNAVAILABLE per AES o QES;
INVALID_REQUEST_BODY per un oggetto mancante o un firmatario malformato; QUOTA_EXCEEDED. Un sigillo o
una marca — chiesti, o richiesti dalla tua organizzazione — sono rifiutati con
FEATURE_NOT_AVAILABLE_IN_BULK (1010): si addebitano al completamento e non si possono riservare in anticipo. Una
sorgente che non si può usare (STORED_FILE_NOT_FOUND) fa fallire il suo elemento, non addebitato, con il codice
della singola chiamata. Ogni elemento dice quale busta ha creato (envelopeId); seguila con
GET /api/v1/sign/envelopes/{id}.
Document Fly in blocco
POST /api/v1/bulk/flow esegue un flusso per elemento: il documento è composto dal
template e mandato in firma nello stesso passo, esattamente come fa una singola POST /api/v1/flow. Vale tutto
quello che c'è in questa pagina: il 202 e il job, l'ammissione per intero o per niente, il worker, gli stati,
Idempotency-Key, i limiti, il CSV, il pacchetto, il nuovo tentativo e il webhook.
POST /api/v1/bulk/flow
Authorization: Bearer lpdc_...
Idempotency-Key: contracts-2026-q4
{
"settings": { "templateId": "tpl_..." },
"signature": { "subject": "Contratto di servizio" },
"items": [
{ "externalId": "C-001",
"data": { "customer": "Acme S.r.l." },
"signers": { "A": { "email": "anna@example.com", "fullName": "Anna Rossi",
"fields": [ { "page": 1, "x": 0.1, "y": 0.8, "width": 0.3, "height": 0.08 } ] } } }
]
}
settingsè la metà di composizione dellesettingsdi un singolo flusso e si applica a ogni elemento;signatureè la parte condivisa del suo blocco di firma (tutto trannesigners). Un elemento porta il suodatae i suoisigners, e può sovrapporre al blocco condiviso il propriosubject,messageedocuments(allegati).- Ogni elemento riserva due cose all'ammissione, un documento e una firma, come un singolo flusso, e
vengono addebitate entrambe quando l'elemento è consegnato. Un elemento che fallisce è rimborsato; un flusso il cui
documento è stato composto prima che la busta fosse rifiutata ha addebitato quel documento una volta (il
creditsConsumeddel job lo conta, per quanti tentativi ci siano), e un nuovo tentativo non lo addebita di nuovo. Un batchisTestspende un elemento di prova per flusso e mai credito. - Il token deve avere
bulk:submiteflow:*.
Rifiutato per intero, con i codici di un singolo flusso: FEATURE_NOT_AVAILABLE_IN_TEST_MODE per una funzione a
pagamento con isTest; SIGNATURE_LEVEL_UNAVAILABLE per AES o QES; DATALESS_NOT_AVAILABLE;
QUOTA_EXCEEDED; FEATURE_NOT_ENABLED per un codice SMS o un livello di firma che la tua organizzazione
non ha acquistato. Un sigillo o una marca sono rifiutati con FEATURE_NOT_AVAILABLE_IN_BULK. Ogni elemento dice
quale documento e quale busta ha creato.
Il job
Un job passa da queued → running → uno tra succeeded,
partially_succeeded, failed o cancelled. Interroga su output.status: è
pending finché il job può ancora cambiare, poi succeeded solo quando ogni elemento ha consegnato, e
failed per tutto il resto (un risultato parziale, un job annullato, uno fallito), quindi leggi
output.bulk.status e gli elementi per distinguerli. Non dedurre lo stato dai conteggi.
| Stato finale | Quando |
|---|---|
succeeded | Ogni elemento ha consegnato un documento. |
partially_succeeded | Alcuni hanno consegnato, altri sono falliti. Il job in sé non è un errore: leggi gli elementi. |
failed | Nessun elemento ha consegnato un documento. |
cancelled | Hai chiesto di fermarlo; prevale sugli altri tre. |
Ogni elemento è queued, processing, succeeded, failed o cancelled.
counts ha un numero per ciascuno, totalItems è quello che hai inviato, e pending è in coda
più in lavorazione.
I risultati si conservano per il periodo indicato in resultsExpireAt (impostato quando il job è finito);
dopo, resultsExpired è true. Prendi quello che ti serve prima.
Leggere i risultati
GET /api/v1/bulk/jobs/6d1e.../items?status=failed&limit=100&after=41
{ "output": { "bulk": {
"jobId": "6d1e...", "...": "il job, come sopra",
"items": [
{ "index": 57, "externalId": "inv-1058", "status": "failed",
"documentId": null, "downloadUrl": null, "creditsUsed": 0,
"error": { "code": "MISSING_TEMPLATE_FIELD", "number": 1401,
"message": "...", "retryable": false },
"warnings": [], "...": "attempts, pageCount, sizeBytes, sha256, durationMs, startedAt, finishedAt" }
],
"page": { "limit": 100, "after": 41, "status": "failed", "nextAfter": 98 } } } }
- Gli elementi arrivano in ordine di
index(la posizione nel tuo batch, da 0).statusfiltra per stato dell'elemento;limitarriva a 500. - Per leggere la pagina successiva, manda
after= ilnextAfterche hai ricevuto. QuandonextAfterènull, era l'ultima pagina. - Un elemento consegnato porta il suo
documentId,downloadUrl, numero di pagine, dimensione e SHA-256. Uno fallito porta l'error.codestabile e il numero (vedi Esiti e codici di errore), eretryabledice se vale la pena rimandare lo stesso elemento. - La piattaforma non ritenta mai un elemento per te. Lo chiedi tu, con la chiamata di
nuovo tentativo; un elemento non da ritentare, o il cui payload non è conservato, lo rimandi in un nuovo batch
(riusa il suo
externalId).
Il pacchetto dei risultati
GET /api/v1/bulk/jobs/{id}/results restituisce uno ZIP quando il job è finito:
documents/<index>.pdf per ogni file prodotto, manifest.json e results.csv (per
elemento: stato, tentativi, documentId / envelopeId, lo SHA-256 del file, numero di pagine, crediti
usati, codice e numero dell'errore, se è da ritentare) e i crediti del job. Il pacchetto di un job di firma contiene solo
il manifesto e il CSV.
- Richiede
bulk:readedocuments:download— consegna gli stessi byte del download per documento. Il job di un'altra organizzazione è un404. - Esiste solo quando il job è finito e finché i suoi risultati sono conservati; altrimenti
BULK_RESULTS_UNAVAILABLE(1504) condetails.reasonjob_not_finishedoresults_expired. Un documento che la tua conservazione ha eliminato è elencato confile: null. - Si costruisce un documento alla volta in un file temporaneo, con un tetto. Un job i cui documenti superano il tetto
è
JOB_TOO_LARGE; scaricalo a intervalli con?after=<index>&limit=<n>(ilrange.nextAfterdel manifesto è quello da mandare dopo). - I nomi delle voci sono l'indice dell'elemento, mai quello che hai scritto tu;
results.csvantepone un apostrofo a ogni testo che un foglio di calcolo potrebbe leggere come formula. - Scaricarlo non costa nulla ed è registrato. Per un documento destinato a qualcuno che non ha una chiave, crea un
link di download firmato per il suo
documentId.
Ritentare gli elementi falliti
POST /api/v1/bulk/jobs/{id}/retry rimette in lavoro gli elementi falliti da ritentare di un job
finito, come nuovi tentativi nello stesso job (attempts li conta, retryCount conta le
chiamate). Ogni elemento è ammesso come uno nuovo: la sua prenotazione, gli stessi limiti e lo stesso worker; un nuovo
tentativo che non si può pagare è rifiutato per intero e non cambia nulla. La risposta è 202 con il job e
retry: {requeued, skippedCount, skipped[]}.
| Un elemento torna in lavoro quando… | altrimenti viene saltato come |
|---|---|
il suo errore dice retryable: true | not_retryable |
| il suo payload è ancora conservato (un'organizzazione che compone senza conservare dati non ne ha) | payload_not_kept |
| ha girato meno di 3 volte | attempts_exhausted |
| nessuna sua richiesta a un fornitore a pagamento ha un esito sconosciuto | outcome_unknown: mai ripetuto, aspetta la sua riconciliazione |
Niente da ritentare è BULK_RETRY_NOT_POSSIBLE (1505) con details.reason
(nothing_retryable, job_not_finished, job_cancelled, results_expired,
payload_not_kept) e gli elementi saltati. È idempotente: una seconda chiamata mentre girano gli elementi della
prima trova il job non finito e non cambia nulla, e con una Idempotency-Key si ripete la prima risposta.
Richiede bulk:submit e l'ambito dell'operazione del job (documents:compose, o signatures:ses
per un job di firma; isTest per un job di prova). Il job finisce di nuovo, e te lo dice di
nuovo.
Webhook di completamento
Aggiungi "callbackUrl": {"onSuccess": "https://...", "onError": "https://..."} a un batch di composizione,
firma o flusso e vieni avvisato quando il job finisce, invece di interrogare. onSuccess è chiamato quando ogni
elemento ha consegnato, onError per ogni altra fine (partially_succeeded, failed,
cancelled). È la stessa consegna di una callback di firma: solo https, firmata
(X-Signer-Signature: HMAC-SHA256 del corpo grezzo con il segreto di callback della tua organizzazione), mai verso
un indirizzo privato o non pubblico, senza seguire reindirizzamenti, ritentata con le stesse attese (fino a circa due
giorni), elencata nel registro dei webhook e ripetibile da lì. Gli header aggiungono
X-Signer-Bulk-Job. Per autenticarti presso il tuo ricevitore, indica un
ambiente di callback e manda percorsi:
"callbackUrl": {"env": "prod", "onSuccess": "/hooks/bulk"}; un env sconosciuto è rifiutato all'invio,
prima di riservare qualunque cosa.
{
"bulkJobId": "6d1e...", "operation": "compose", "status": "partially_succeeded",
"isTest": false, "totalItems": 100, "counts": { "succeeded": 97, "failed": 3, "...": "..." },
"creditsConsumed": 485, "creditsReleased": 15, "retryCount": 0,
"reconciliation": { "...": "come nel job" }, "links": { "self": "...", "items": "...", "results": "..." },
"event": { "id": "evt_...", "type": "bulk.partially_succeeded", "occurredAt": "...", "schema": 1 }
}
event.id è stabile tra i tentativi: elimina i doppioni su quello. Il corpo nomina il job e i numeri e non
porta nessun dato degli elementi; leggi gli elementi, a pagine. Un job ritentato che finisce di nuovo manda
un nuovo evento. L'URL non viene mai rimandato dall'API.
Crediti: credits.total e credits.block
Come ogni risposta, una risposta bulk porta output.credits come {used, left}. Per un job,
used è il totale di quello che i suoi elementi hanno regolato finora (la somma dei creditsUsed per
elemento; 0 per isTest), e left è il tuo saldo letto dal registro adesso — mai il tuo
saldo precedente meno used, che è sbagliato finché il job tiene una prenotazione.
Dentro output.bulk lo stesso denaro è scomposto per il job:
| Campo | Significato |
|---|---|
creditsReserved | Quanto è stato messo da parte per il batch all'ammissione: il prezzo di un documento per elemento. |
creditsConsumed | Quanto sono costati davvero i documenti consegnati. |
creditsHeld | Ancora riservato per elementi non finiti. 0 quando il job è finito. |
creditsReleased | Restituito: elementi falliti o annullati. |
Quando il job è finito porta anche una reconciliation:
"reconciliation": {
"requested": 100, "succeeded": 97,
"failed": { "platform": 1, "customer": 1 },
"cancelled": 1,
"creditsReserved": 500, "creditsConsumed": 485, "creditsReleased": 15,
"chargedFailureClasses": []
}
creditsConsumed + creditsReleased = creditsReserved. Gli errori sono divisi in platform (nostri: un
errore interno, un problema di composizione, la scadenza di un job) e customer (l'input), così vedi quale è
quale; nessuno dei due si paga.
Cosa si paga
Un documento si paga se, e solo se, è stato consegnato. Un job bulk costa esattamente quanto gli stessi documenti mandati uno alla volta.
| Esito | Addebito |
|---|---|
| Il batch è rifiutato (malformato, oltre un limite, credito insufficiente, nessuna capacità) | Non si riserva nulla, non si addebita nulla. |
| Un elemento fallisce per i suoi dati o per il template | Non si paga; il suo credito torna indietro. |
| Un elemento fallisce per un motivo della piattaforma | Non si paga; il suo credito torna indietro. |
| Un elemento è annullato prima di iniziare, o è ancora in attesa alla scadenza del job | Non si paga; il suo credito torna indietro. |
| Un elemento è consegnato | Si paga un documento. |
Un elemento isTest | Usa la dotazione di prova, mai credito. Vedi sotto. |
Il credito di tutto il batch è riservato in un passo prima di comporre qualunque documento, così due batch che insieme
superano il tuo saldo non possono essere ammessi entrambi: il secondo è rifiutato con QUOTA_EXCEEDED (vedi
Esiti e codici di errore) e non resta nulla dietro. È riservato per elemento, e ogni elemento si
regola da solo quando finisce.
Annullare
POST /api/v1/bulk/jobs/{id}/cancel ferma il job. Gli elementi non ancora iniziati sono annullati e il loro
credito è restituito subito; un elemento in composizione in quel momento finisce ed è addebitato se consegna. La risposta è
il job (cancelRequested è true). Annullare un job già finito non cambia nulla e risponde con il job.
La chiamata prende una Idempotency-Key come le altre.
Idempotenza
Inviare, caricare un file, annullare e ritentare prendono tutti una Idempotency-Key, e per un invio dovresti
mandarne sempre una. Con la chiave, un nuovo tentativo di una richiesta andata in timeout restituisce lo stesso
job (la risposta porta Idempotent-Replay: true) invece di ammettere e addebitare un secondo batch. La
stessa chiave con un corpo diverso è IDEMPOTENCY_KEY_REUSED. Le regole sono quelle di
ogni altra chiamata; usa una chiave del tuo dominio, come il riferimento di un lotto o di un
periodo.
Prove: isTest
Con "isTest": true in settings tutto il batch gira nella sandbox: non si spende credito e i
documenti sono segnati come prove. Il batch prende un elemento della dotazione di prova per documento dalla
stessa riserva delle singole chiamate di prova, tutto o niente, ed è limitato a pochi elementi per batch (oggi il
predefinito è 5). Un batch di prova oltre il limite è BULK_ITEM_LIMIT_EXCEEDED. Gli elementi di prova vengono
dopo il lavoro di produzione.
Organizzazioni dataless
Se la tua organizzazione compone dataless, un batch bulk conserva comunque i suoi
documenti per la tua normale conservazione — un batch risponde senza byte, quindi li scarichi dopo — e la risposta
porta l'avviso DOCUMENT_STORED_FOR_BULK (2203). I dati che hai inviato non vengono registrati; il payload di ogni
elemento viene cancellato quando l'elemento finisce.
Limiti
Questi sono i valori predefiniti attuali. Sono valori di lavoro e possono cambiare; quelli che valgono per la tua organizzazione possono essere più bassi, mai più alti. Una richiesta è rifiutata per intero, senza riservare nulla, quando ne supera uno.
| Limite | Predefinito | Rifiutato con |
|---|---|---|
| Elementi in un batch in linea | 500 | BULK_ITEM_LIMIT_EXCEEDED |
| Elementi in un file caricato | 2.000 | BULK_ITEM_LIMIT_EXCEEDED |
| Dimensione di un corpo in linea / di un file caricato | 10 MiB / 25 MB (nessuna richiesta può essere più grande) | JOB_TOO_LARGE / PAYLOAD_TOO_LARGE |
Elementi in un batch isTest | 5 | BULK_ITEM_LIMIT_EXCEEDED |
| Job in esecuzione contemporanea | 2 | BULK_CONCURRENCY_LIMIT_REACHED |
| Elementi in attesa nei tuoi job | 20.000 | BULK_CONCURRENCY_LIMIT_REACHED |
| Invii al minuto | 6 | BULK_CONCURRENCY_LIMIT_REACHED |
| File caricati non ancora usati | 5 | BULK_CONCURRENCY_LIMIT_REACHED |
Il limite effettivo di elementi è il più basso tra quello che chiedi in options.maxItems, il limite della tua
organizzazione e quello della piattaforma; details.requestedItems e details.allowedItems dicono di
quanto eri oltre. BULK_CONCURRENCY_LIMIT_REACHED è da ritentare e porta Retry-After:
details.what dice quale limite era. Un job ancora in esecuzione sei ore dopo l'ammissione fa scadere gli elementi
rimasti (BULK_JOB_TIMED_OUT, non addebitati). Gli elementi si compongono uno dopo l'altro e condividono la
capacità con le richieste di tutti gli altri, quindi un batch grande è questione di minuti, non di secondi; la piattaforma
mette in pausa un job invece di farlo fallire quando è occupata. Se il bulk è spento per tutto il servizio, un nuovo batch è
rifiutato con SERVICE_PAUSED.
Ambiti
Un batch di composizione richiede bulk:submit e documents:compose; un batch di firma
bulk:submit e signatures:ses; un batch di flussi bulk:submit e
flow:*. Il primo autorizza a eseguire un batch, il secondo l'operazione al suo interno, e un rifiuto
(TOKEN_SCOPE_NOT_ALLOWED, 403) nomina il primo ambito mancante in details.missingScopes. Il gruppo di
ambiti bulk è: bulk:submit per inviare, caricare, annullare e ritentare, bulk:read per leggere
job, elementi e il pacchetto dei risultati (che richiede anche documents:download), o bulk:* per
entrambi. Spuntare bulk:submit non spunta per te l'ambito dell'operazione: spuntali entrambi nella pagina
Chiavi API. I token emessi prima che esistesse il bulk non hanno questi ambiti; emetti un nuovo
token con gli ambiti spuntati. Il job o il file di un'altra organizzazione risponde 404, esattamente come uno che
non esiste.
Nella console
Un batch è un elenco di singole chiamate, e la console lo mostra così. Ogni documento creato da un batch di composizione
è una riga di Documenti e ogni busta inviata da un batch di firma è una riga di Firme, segnata
Batch con un link al suo batch; il filtro sopra ogni elenco mostra tutte le righe, solo le singole chiamate o solo i
batch. Un batch avviato con una chiave API c'è senza dover configurare nulla (Documenti, poi
Batch, su /tenant/batches). I risultati di un batch si conservano per il periodo di conservazione;
dopo, i suoi documenti sono ancora in Documenti, ma non più segnati.
La pagina del batch mostra stato, tipo, quanti elementi sono fatti, falliti, annullati e in attesa, una barra di
avanzamento, e i crediti riservati, usati e restituiti (le stesse cifre di output.credits; un batch di prova lo
dice e non mostra credito). Ogni elemento porta al suo documento o alla sua richiesta di firma, e uno fallito mostra il suo
codice di errore stabile, il numero e il messaggio. Chiunque veda Documenti può leggere la pagina e scaricare lo ZIP dei
risultati. Un coordinatore può anche ritentare gli elementi falliti (solo quelli da ritentare, come fa
l'API), annullare un batch in corso (quello che non era iniziato si ferma e il suo credito torna) e
rimandare il webhook di completamento; ognuna delle tre cose è registrata nel registro delle attività con il
nome della persona.
Non ancora disponibile
- Niente sigillo, marca temporale o password su un documento o una busta in blocco: si addebitano o si conservano a cose fatte e non si possono riservare in anticipo (manda quelli uno alla volta).
- Niente AES o QES, né in un batch né singolarmente (
SIGNATURE_LEVEL_UNAVAILABLE). - Un link di download firmato per tutto il pacchetto: lo ZIP si scarica con la tua chiave; un link si crea per documento.