SIGN PDF API · GO
Sign a PDF in Go.
This sign PDF API in Go example sends a template to one person, who signs on any device. Make a test key and a template, then run it. A test key sends nothing real and bills nothing.
SIGNED
Signed
GO
package main
import (
"context"
"fmt"
"os"
"time"
docustay "github.com/docustay/docustay-go/docustay"
)
func main() {Send, listen, download
Three steps, one program.
1 · Install
Add the client.
go get github.com/docustay/docustay-go/docustay@v0.2.2
The module needs Go 1.23 or newer and has no dependencies beyond the standard library. Every call that creates something takes an idempotency key: build one per attempt and reuse it when you retry. Pass the server only when you point at a self-hosted install. The client is generated from the OpenAPI document, so every operation is there.
2 · Send
send.go
package main
import (
"context"
"fmt"
"os"
"time"
docustay "github.com/docustay/docustay-go/docustay"
)
func main() {
cfg := docustay.NewConfiguration()
server := "https://app.docustay.app"
if u := os.Getenv("DOCUSTAY_URL"); u != "" {
server = u
}
cfg.Servers = docustay.ServerConfigurations{{URL: server}}
client := docustay.NewAPIClient(cfg)
ctx := context.WithValue(context.Background(), docustay.ContextAccessToken, os.Getenv("DOCUSTAY_API_KEY"))
request := *docustay.NewSendRequest(os.Getenv("DOCUSTAY_TEMPLATE_ID"), []docustay.SendRequestPartiesInner{
*docustay.NewSendRequestPartiesInner("signer_1", "Rosa Alvarez", "rosa@example.com"),
})
doc, _, err := client.DocumentsAPI.SendDocument(ctx).SendRequest(request).IdempotencyKey(fmt.Sprintf("send-%d", time.Now().UnixNano())).Execute()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(doc.GetId(), doc.GetState())
} This sends the template to one person. It reads the key and the template id from the environment.
3 · Then
Read the answer.
The Go program's answer holds the document's id and its state. Ask for it later with the same key, or get a signed webhook when it is signed. The quickstart shows the rest.
What happens next
A typed client generated from the API description: every operation is there.
The person you named signs on a phone or a computer from the link.
From Northwind Studio <hello@northwind.example>
Subject Please sign: Services Agreement
Dana, Northwind Studio has asked you to review and sign a document.
The link is yours alone. You do not need an account.
The request, by hand
What the Go program sends.
| Part | Value |
|---|---|
| Method and path | POST /api/v1/documents/send |
| Authorization | Bearer, then your API key |
| Content-Type | application/json |
| Idempotency-Key | A fresh UUID for each document; keep it if you retry |
| Body | templateId and parties: a seat, a name and an email for each person |
The same request works from any language that can make an HTTPS call.
Line by line
What the Go program does.
Read it once and you can change it with confidence.
The standard library only
The example imports bytes, fmt, io, net/http and os, so there is nothing to download and no module file to keep. Save it as send.go and run it with go run.
Build the POST
http.NewRequest points at /api/v1/documents/send with the JSON body, and Header.Set adds the bearer key, the JSON content type and the Idempotency-Key.
Make the key a real random value
The example makes the key from the process id to stay short. In your own program make it from a random value and keep it if you retry, because a new key would mean a second document.
Check the status before you print
A status of 300 or more stops with the status and the body; otherwise the program prints the answer, which holds the document's id and its state.
When it goes wrong
Errors and retries in Go.
Every error from the API has the same shape: kind, a plain message, a requestId and a retryable flag.
Read the error object yourself
With plain HTTP there is no exception type: on a failure the body is a JSON object with a kind, a message, a requestId and a retryable flag, and the status says which family it is. The example prints the body so you can see them.
Retries are yours to write
Retry a 429 after the seconds in Retry-After, and a 5xx after a pause, always with the same Idempotency-Key. Do not retry a 400, 401, 403, 404 or 409: they will give the same answer. A generated Go client is available if you would rather not write that loop.
What is safe to retry
In Go, as anywhere, a 429 and a 5xx are safe to retry with the same Idempotency-Key: sending the same request again never sends a second document. A 400, 401, 403, 404 or 409 will give the same answer every time, so fix the cause before you try again.
Before you run it
What you need on hand.
Three things, none of them hard to get.
Any recent Go
No module is needed for this file. The standard library does the whole job.
A test key
Make a key in the app under Developers, then API keys, and tick documents:read and documents:write. Give it to the Go program through DOCUSTAY_API_KEY. A key that starts dsk_test_ only does practice runs: the email goes to you and nothing counts.
A template id
Use your own copy of a built-in template, or one you made from HTML or a tagged PDF, and put its id in DOCUSTAY_TEMPLATE_ID before you run the Go file. Its seat names, such as signer_1, are what the parties in the request refer to.
Run it
Save the program as send.go. It prints the document's id and its state, and the test email arrives in your own inbox within a minute or so.
Questions
Quick answers.
Does a test key send anything real?
No. A document sent with a dsk_test_ key is emailed only to you, carries a TEST watermark and is never counted.
Where do the key and template id come from?
From the environment: DOCUSTAY_API_KEY and DOCUSTAY_TEMPLATE_ID. The key never sits in the file.
How do I know it was signed?
Subscribe to documents.document.executed, or fetch the document by id.