curl --request GET \
--url https://app.vumasign.com/api/v1/envelopes \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.vumasign.com/api/v1/envelopes"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.vumasign.com/api/v1/envelopes', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.vumasign.com/api/v1/envelopes"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"data": [
{
"id": "01960000-0000-4000-8000-0000000000e5",
"title": "Employment contract",
"status": "sent",
"template_id": "01960000-0000-4000-8000-0000000007e1",
"created_at": "2026-09-02T09:00:00.000Z",
"sent_at": "2026-09-02T09:05:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}List envelopes, newest first, optionally filtered by status.
GET /api/v1/envelopes/{envelopeId} reads one envelope by an id you already hold; this is how you find out which ids exist. The webhook delivery ladder gives up after 7 attempts, so an integrator who dropped a delivery has no way to learn what was missed except by asking here.
Walked by keyset cursor rather than by offset, for the same reason GET /api/v1/templates is: this list is ordered newest first, so an envelope created between page 1 and page 2 would shift every row down under an offset, and a caller would receive one row twice and never see another.
⚠️ cursor IS OPAQUE. It is the last envelope’s id in clear, but that is an implementation detail rather than a promise — pass back exactly the next_cursor a previous page gave you and never construct one by hand. A well-formed uuid naming no envelope of yours (yours or another organisation’s — this endpoint cannot tell the two apart, and does not try) is refused rather than answered with an empty page, for the same reason an unknown status is below: an empty page reads as “you have none”, which is the single hardest defect class to notice because it looks exactly like data.
EACH ROW IS A SUMMARY, not the full Envelope GET /api/v1/envelopes/{envelopeId} returns — no recipients, no delivery state. Read one envelope in full once you know which one you want.
curl --request GET \
--url https://app.vumasign.com/api/v1/envelopes \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.vumasign.com/api/v1/envelopes"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.vumasign.com/api/v1/envelopes', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.vumasign.com/api/v1/envelopes"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"data": [
{
"id": "01960000-0000-4000-8000-0000000000e5",
"title": "Employment contract",
"status": "sent",
"template_id": "01960000-0000-4000-8000-0000000007e1",
"created_at": "2026-09-02T09:00:00.000Z",
"sent_at": "2026-09-02T09:05:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}{
"error": {
"code": "unauthenticated",
"message": "<string>"
}
}Authorizations
Authorization: Bearer vsk_live_…. Chosen over a bespoke X-API-KEY header because every client, proxy and log-redaction rule already knows this one. What a key may DO is its scopes — see x-scopes at the root of this document and x-required-scope on each operation. The scope list is not written here because OpenAPI reserves a requirement’s scope array for oauth2 and openIdConnect and requires it to be empty for an http scheme.
Query Parameters
⚠️ A VALUE OUTSIDE THE RANGE IS REFUSED, NOT CLAMPED, for the same reason GET /api/v1/templates refuses one: a caller who asks for 1000 and receives 100 has a working integration reading their whole account in ten times the requests they budgeted, and nothing anywhere says so.
How many envelopes to return.
1 <= x <= 100The next_cursor from the previous page. Omit it to start at the newest envelope. Opaque — see above.
An envelope id to resume after.
⚠️ AN UNKNOWN VALUE IS REFUSED, NOT ANSWERED WITH AN EMPTY PAGE. A typo — "complete" for "completed" — would otherwise read as "you have none", which is data-shaped and is exactly the failure this refusal exists to avoid. Omit it to list every status.
Restrict the list to one status.
draft, sent, partially_signed, completed, declined, voided, expired Response
A page of envelope summaries, newest first, with the cursor for the next.
The page.
This page’s envelopes.
Show child attributes
Show child attributes
⚠️ THE ANSWER, rather than something to infer. A caller inferring "more" from a full page loops one extra time on every organisation whose envelope count is an exact multiple of limit.
Pass this as cursor for the next page. Null exactly when has_more is false -- the two are computed from one expression, so a true with a null cursor cannot happen. ⚠️ OPAQUE: it is the last envelope’s id, but that is an implementation detail rather than a promise -- pass it back exactly as received and never construct one by hand.