Mit DocuGenerate können Sie PDF- und Word-Dokumente direkt aus Ihrer Go-Anwendung erstellen. Diese Anleitung zeigt, wie Sie jede API-Methode ab Go 1.18 aufrufen. Die vollständige Liste der Parameter und Antworten finden Sie in der API-Referenz.
1. Authentifizierung
2. Vorlage erstellen
3. Vorlagen auflisten
4. Vorlage abrufen
5. Vorlage aktualisieren
6. Vorlage löschen
7. Dokument generieren
8. Dokumente auflisten
9. Dokument abrufen
10. Dokument aktualisieren
11. Dokument löschen
Jede Anfrage wird authentifiziert, indem Sie Ihren API-Schlüssel im Header Authorization senden. Speichern Sie den Schlüssel in einer Umgebungsvariable, statt ihn fest in Ihren Quellcode zu schreiben:
export DOCUGENERATE_API_KEY="YOUR-API-KEY"
Die Beispiele verwenden die Pakete net/http, mime/multipart und encoding/json aus der Go-Standardbibliothek, sodass nichts installiert werden muss. Jedes Beispiel ist als Rumpf der Funktion main eines Programms mit diesen Importen und Deklarationen geschrieben. Entfernen Sie die Importe, die ein Beispiel nicht verwendet, da Go ungenutzte Importe nicht kompiliert. Da net/http bei HTTP-Fehlerstatus keinen Fehler zurückgibt, prüft jedes Beispiel den Statuscode, bevor es die Antwort liest:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
const apiURL = "https://api.docugenerate.com/v1"
var apiKey = os.Getenv("DOCUGENERATE_API_KEY")
func main() {
// Add the example code here
}
Wenn Ihr Konto Daten in einer anderen Region speichert, ersetzen Sie die Basis-URL durch den passenden regionalen Endpunkt, zum Beispiel https://api.eu.docugenerate.com/v1.
Um eine Vorlage zu erstellen, laden Sie die Vorlagendatei mit einer Anfrage an POST /template hoch. Dieser Endpunkt erfordert den Inhaltstyp multipart/form-data, der hier mit einem multipart.Writer erzeugt wird:
file, err := os.Open("Business Letter.docx")
if err != nil {
panic(err)
}
defer file.Close()
var form bytes.Buffer
writer := multipart.NewWriter(&form)
part, err := writer.CreateFormFile("file", "Business Letter.docx")
if err != nil {
panic(err)
}
if _, err := io.Copy(part, file); err != nil {
panic(err)
}
writer.WriteField("name", "Business Letter")
writer.Close()
request, err := http.NewRequest("POST", apiURL+"/template", &form)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", writer.FormDataContentType())
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
var template map[string]any
if err := json.Unmarshal(body, &template); err != nil {
panic(err)
}
fmt.Println(template["id"])
Setzen Sie den Header Content-Type immer mit writer.FormDataContentType(), das die Multipart-Boundary enthält. Ohne die Boundary schlägt die Anfrage fehl. Die Antwort enthält die neue Vorlage, einschließlich der automatisch in der Datei erkannten tags:
{
"enhanced_syntax": false,
"versioning_enabled": false,
"folder": [],
"tags": {
"valid": [
"Date",
"Name",
"Job Title",
"Company Name",
"Street Address",
"City",
"State",
"Zip Code",
"Email",
"Phone"
],
"invalid": []
},
"created": 1791055374301,
"updated": 1791055374301,
"name": "Business Letter",
"delimiters": {
"left": "[",
"right": "]"
},
"filename": "Business Letter.docx",
"format": ".docx",
"region": "eu",
"page_count": 1,
"image_uri": "https://firebasestorage.googleapis.com/v0/b/storage.eu.docugenerate.com/o/templates%2FuVE30i1427KQsYcED0bl%2FBusiness%20Letter.png?alt=media&token=0ce64a4b-495a-426b-8783-42b479f7ae38",
"preview_uri": "https://firebasestorage.googleapis.com/v0/b/storage.eu.docugenerate.com/o/templates%2FuVE30i1427KQsYcED0bl%2FBusiness%20Letter.pdf?alt=media&token=0b3939c7-824e-4979-9aca-ca4a87175cbf",
"template_uri": "https://firebasestorage.googleapis.com/v0/b/storage.eu.docugenerate.com/o/templates%2FuVE30i1427KQsYcED0bl%2FBusiness%20Letter.docx?alt=media&token=d37a4458-3620-48db-94b8-6ac46f0442ab",
"id": "uVE30i1427KQsYcED0bl"
}
Bewahren Sie die id der Vorlage auf, da Sie sie zum Generieren von Dokumenten benötigen. Die folgenden optionalen Parameter können ebenfalls in das Formular geschrieben werden:
delimiters: Die Begrenzer zur Erkennung der Tags, als JSON-String gesendet, z. B. {"left": "[", "right": "]"}. Standardmäßig werden sie automatisch ermittelt.region: Wo die Vorlage und ihre generierten Dokumente gespeichert werden, entweder us, eu, uk oder au. Standardmäßig wird die Region des Kontos verwendet.enhanced_syntax: Auf "true" setzen, um verschachtelte Eigenschaften und logische oder mathematische Operatoren in den Tags zu verwenden.versioning_enabled: Auf "true" setzen, um beim Hochladen einer neuen Datei frühere Versionen zu behalten, sofern Ihr Plan dies erlaubt.folder: Der Ordner der Vorlage, von der Wurzel bis zur untersten Ebene. Fehlende Ordner werden automatisch erstellt.Um die Vorlage in einem Ordner abzulegen, schreiben Sie für jede Ebene des Pfads ein folder-Feld. Zum Beispiel, um sie im Ordner Letters > Business abzulegen:
writer.WriteField("folder", "Letters")
writer.WriteField("folder", "Business")
Eine Anfrage an GET /template gibt alle Vorlagen in Ihrem Konto zurück:
request, err := http.NewRequest("GET", apiURL+"/template", nil)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
var templates []map[string]any
if err := json.Unmarshal(body, &templates); err != nil {
panic(err)
}
for _, template := range templates {
fmt.Println(template["id"], template["name"])
}
Um nur die Vorlagen eines Ordners aufzulisten, wiederholen Sie den Abfrageparameter folder für jede Ebene des Pfads. Zum Beispiel, um die Vorlagen im Ordner Letters > Business aufzulisten:
request, err := http.NewRequest("GET", apiURL+"/template?folder=Letters&folder=Business", nil)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
Es werden nur die Vorlagen zurückgegeben, die direkt in diesem Ordner liegen. Vorlagen in seinen Unterordnern sind nicht enthalten. Erfahren Sie mehr darüber, wie Sie mit der API Vorlagen in Ordnern organisieren.
Um eine einzelne Vorlage abzurufen, rufen Sie GET /template/{id} mit ihrer ID auf:
templateID := "bet2oQirk0pSd9ctH9Qu"
request, err := http.NewRequest("GET", apiURL+"/template/"+templateID, nil)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
var template map[string]any
if err := json.Unmarshal(body, &template); err != nil {
panic(err)
}
fmt.Println(template["tags"].(map[string]any)["valid"])
Das ist zum Beispiel nützlich, um vor dem Generieren von Dokumenten zu prüfen, welche Zusammenführungs-Tags die Vorlage erwartet.
Eine Anfrage an PUT /template/{id} aktualisiert eine Vorlage. Alle Parameter sind optional, senden Sie also nur die, die Sie ändern möchten. Zum Beispiel, um eine neue Version der Datei hochzuladen und die Vorlage umzubenennen:
templateID := "bet2oQirk0pSd9ctH9Qu"
file, err := os.Open("Business Letter v2.docx")
if err != nil {
panic(err)
}
defer file.Close()
var form bytes.Buffer
writer := multipart.NewWriter(&form)
part, err := writer.CreateFormFile("file", "Business Letter v2.docx")
if err != nil {
panic(err)
}
if _, err := io.Copy(part, file); err != nil {
panic(err)
}
writer.WriteField("name", "Business Letter v2")
writer.Close()
request, err := http.NewRequest("PUT", apiURL+"/template/"+templateID, &form)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", writer.FormDataContentType())
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
var template map[string]any
if err := json.Unmarshal(body, &template); err != nil {
panic(err)
}
Wie beim Erstellen einer Vorlage muss der Body multipart/form-data sein, mit dem Header Content-Type auf writer.FormDataContentType() gesetzt. Neben file und name können die folgenden optionalen Parameter in das Formular geschrieben werden:
delimiters: Die neuen Begrenzer, als JSON-String gesendet. Wenn sie angegeben werden, wird die Vorlage erneut analysiert, um die Zusammenführungs-Tags anhand der neuen Begrenzer zu erkennen. Wird ein neues file ohne delimiters hochgeladen, werden die aktuellen Begrenzer verwendet.region: Verschiebt die Vorlage in eine andere Region, entweder us, eu, uk oder au. Danach generierte Dokumente werden in der neuen Region gespeichert, während bestehende Dokumente in ihrer aktuellen Region bleiben.folder: Verschiebt die Vorlage in einen anderen Ordner, mit einem folder-Feld für jede Ebene des Pfads. Senden Sie "[]", um die Vorlage aus jedem Ordner herauszunehmen.enhanced_syntax: Auf "true" oder "false" setzen, um die erweiterte Syntax zu aktivieren oder zu deaktivieren.versioning_enabled: Auf "true" oder "false" setzen, um den Versionsverlauf zu aktivieren oder zu deaktivieren, sofern Ihr Plan dies erlaubt.Um eine Vorlage zu löschen, senden Sie eine Anfrage an DELETE /template/{id}. Die API antwortet bei Erfolg mit dem Status 204 No Content:
templateID := "bet2oQirk0pSd9ctH9Qu"
request, err := http.NewRequest("DELETE", apiURL+"/template/"+templateID, nil)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
Sie generieren Dokumente mit einer Anfrage an POST /document, wobei Sie die template_id und die data übergeben, mit denen die Zusammenführungs-Tags ersetzt werden:
payload, err := json.Marshal(map[string]any{
"template_id": "bet2oQirk0pSd9ctH9Qu",
"data": map[string]string{
"Date": "October 4, 2026",
"Name": "Emily Carter",
"Job Title": "Operations Manager",
"Company Name": "Harbor Point Consulting",
"Street Address": "118 West Street",
"City": "Annapolis",
"State": "Maryland",
"Zip Code": "21405",
"Email": "emily.carter@example.com",
"Phone": "(410) 555-0142",
},
"output_format": ".pdf",
})
if err != nil {
panic(err)
}
request, err := http.NewRequest("POST", apiURL+"/document", bytes.NewReader(payload))
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
var document map[string]any
if err := json.Unmarshal(body, &document); err != nil {
panic(err)
}
fmt.Println(document["document_uri"])
Die Antwort enthält die Eigenschaften des Dokuments:
{
"created": 1791125416372,
"template_id": "bet2oQirk0pSd9ctH9Qu",
"name": "Business Letter",
"format": ".pdf",
"data_length": 1,
"filename": "Business Letter.pdf",
"document_uri": "https://firebasestorage.googleapis.com/v0/b/storage.us.docugenerate.com/o/documents%2FiESelthRt4uaYQRTemrL%2FBusiness%20Letter.pdf?alt=media&token=c4b259ec-249b-4b35-a62f-6a8dc0da75f3",
"id": "iESelthRt4uaYQRTemrL"
}
Das output_format kann .docx (Standard), .pdf, .doc, .odt, .txt, .html, .png oder eine PDF/A-Version sein. Sie können außerdem mit merge_with PDF-Dateien am Ende des generierten Dokuments zusammenführen oder mit attach Anhänge hinzufügen.
Datei herunterladen
Die document_uri verweist auf die generierte Datei, die Sie herunterladen und auf der Festplatte speichern können:
file, err := http.Get(document["document_uri"].(string))
if err != nil {
panic(err)
}
defer file.Body.Close()
if file.StatusCode >= 400 {
panic(fmt.Sprintf("Download failed with status %d", file.StatusCode))
}
content, err := io.ReadAll(file.Body)
if err != nil {
panic(err)
}
if err := os.WriteFile(document["filename"].(string), content, 0644); err != nil {
panic(err)
}
Datei direkt empfangen
Wenn das Dokument nicht in der Cloud gespeichert werden soll, setzen Sie den Header Accept auf application/octet-stream. Die API antwortet dann mit der Binärdatei statt mit JSON:
payload, err := json.Marshal(map[string]any{
"template_id": "bet2oQirk0pSd9ctH9Qu",
"data": map[string]string{
"Date": "October 4, 2026",
"Name": "Emily Carter",
"Job Title": "Operations Manager",
"Company Name": "Harbor Point Consulting",
"Street Address": "118 West Street",
"City": "Annapolis",
"State": "Maryland",
"Zip Code": "21405",
"Email": "emily.carter@example.com",
"Phone": "(410) 555-0142",
},
"output_format": ".pdf",
})
if err != nil {
panic(err)
}
request, err := http.NewRequest("POST", apiURL+"/document", bytes.NewReader(payload))
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/octet-stream")
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
if err := os.WriteFile("Business Letter.pdf", body, 0644); err != nil {
panic(err)
}
fmt.Println(response.Header.Get("X-Document-Id"))
Batch-Dokumentgenerierung
Um mehrere Dokumente in einer Anfrage zu generieren, übergeben Sie einen Slice von Maps als data. Für jede Map wird ein Dokument generiert:
payload, err := json.Marshal(map[string]any{
"template_id": "bet2oQirk0pSd9ctH9Qu",
"data": []map[string]string{
{"Date": "October 4, 2026", "Name": "Emily Carter", "Job Title": "Operations Manager", "Company Name": "Harbor Point Consulting", "Street Address": "118 West Street", "City": "Annapolis", "State": "Maryland", "Zip Code": "21405", "Email": "emily.carter@example.com", "Phone": "(410) 555-0142"},
{"Date": "October 4, 2026", "Name": "Daniel Brooks", "Job Title": "Logistics Coordinator", "Company Name": "Northfield Logistics", "Street Address": "2400 South Lamar Boulevard", "City": "Austin", "State": "Texas", "Zip Code": "78704", "Email": "daniel.brooks@example.com", "Phone": "(512) 555-0187"},
},
"output_format": ".pdf",
"single_file": true,
"page_break": true,
})
if err != nil {
panic(err)
}
request, err := http.NewRequest("POST", apiURL+"/document", bytes.NewReader(payload))
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
var document map[string]any
if err := json.Unmarshal(body, &document); err != nil {
panic(err)
}
Standardmäßig werden alle Dokumente in einer einzigen Datei zusammengefasst, mit einem Seitenumbruch nach jedem Dokument. Setzen Sie page_break auf false, um die Seitenumbrüche zu entfernen.
Wenn single_file auf false gesetzt ist, wird pro Datenobjekt eine Datei generiert, und alle Dateien werden in einem .zip-Archiv zusammengefasst. Verwenden Sie den Parameter name, um das Archiv zu benennen, und output_name mit Zusammenführungs-Tags, um jeder Datei einen dynamischen Namen zu geben, z. B. Letter for [Name]:
payload, err := json.Marshal(map[string]any{
"template_id": "bet2oQirk0pSd9ctH9Qu",
"data": []map[string]string{...},
"output_format": ".pdf",
"single_file": false,
"name": "Business Letters",
"output_name": "Letter for [Name]",
})
Dadurch entsteht ein Archiv Business Letters.zip mit Letter for Emily Carter.pdf und Letter for Daniel Brooks.pdf. Die Zusammenführungs-Tags in output_name müssen dieselben Begrenzer wie die Vorlage verwenden.
Datendatei verwenden
Um viele Dokumente auf einmal aus einer Excel- oder CSV-Datei zu generieren, senden Sie die Datei in einer multipart/form-data-Anfrage. Für jede Zeile der Tabelle wird ein Dokument generiert. Wenn die Datei mehrere Tabellenblätter enthält, geben Sie mit dem Parameter sheet an, welches verwendet werden soll.
file, err := os.Open("Data.xlsx")
if err != nil {
panic(err)
}
defer file.Close()
var form bytes.Buffer
writer := multipart.NewWriter(&form)
writer.WriteField("template_id", "bet2oQirk0pSd9ctH9Qu")
part, err := writer.CreateFormFile("file", "Data.xlsx")
if err != nil {
panic(err)
}
if _, err := io.Copy(part, file); err != nil {
panic(err)
}
writer.WriteField("output_format", ".pdf")
writer.Close()
request, err := http.NewRequest("POST", apiURL+"/document", &form)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", writer.FormDataContentType())
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
Die Verwendung einer Datendatei ist eine weitere Form der Batch-Generierung, daher gelten dieselben Parameter, um die generierten Dokumente in einer einzigen Datei zusammenzufassen oder sie in einem .zip-Archiv mit einem eigenen Namen für jede Datei zu gruppieren.
Eine Anfrage an GET /document gibt die aus einer Vorlage generierten Dokumente zurück. Die ID der Vorlage wird im Abfrageparameter template_id übergeben:
request, err := http.NewRequest("GET", apiURL+"/document?template_id=bet2oQirk0pSd9ctH9Qu", nil)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
var documents []map[string]any
if err := json.Unmarshal(body, &documents); err != nil {
panic(err)
}
for _, document := range documents {
fmt.Println(document["id"], document["name"], document["document_uri"])
}
Um ein einzelnes Dokument abzurufen, rufen Sie GET /document/{id} mit seiner ID auf:
documentID := "iESelthRt4uaYQRTemrL"
request, err := http.NewRequest("GET", apiURL+"/document/"+documentID, nil)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
var document map[string]any
if err := json.Unmarshal(body, &document); err != nil {
panic(err)
}
Eine Anfrage an PUT /document/{id} benennt ein Dokument um, da der Name die einzige Eigenschaft ist, die geändert werden kann:
documentID := "iESelthRt4uaYQRTemrL"
payload, err := json.Marshal(map[string]string{"name": "Letter for Emily Carter"})
if err != nil {
panic(err)
}
request, err := http.NewRequest("PUT", apiURL+"/document/"+documentID, bytes.NewReader(payload))
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}
var document map[string]any
if err := json.Unmarshal(body, &document); err != nil {
panic(err)
}
Um ein Dokument zu löschen, senden Sie eine Anfrage an DELETE /document/{id}. Die API antwortet bei Erfolg mit dem Status 204 No Content:
documentID := "iESelthRt4uaYQRTemrL"
request, err := http.NewRequest("DELETE", apiURL+"/document/"+documentID, nil)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", apiKey)
request.Header.Set("Accept", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode >= 400 {
panic(fmt.Sprintf("DocuGenerate API error %d: %s", response.StatusCode, body))
}