> For the complete documentation index, see [llms.txt](https://developers.docstudio.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.docstudio.com/guides/draft-envelopes.md).

# Draft Envelopes

This document contains endpoints for creating, updating, and deleting draft envelopes, including headers, request bodies, responses, and practical examples.

Drafts allow an integration to prepare envelope XML, assign recipients, store metadata, and save the result before starting the document workflow.

The endpoints on this page create, update, and delete draft envelopes. To create and send an envelope immediately, use [Send Envelope](/api-reference/send-envelope.md) instead.

### Business goals

Draft Envelope endpoints support integrations that need to prepare an envelope over multiple operations instead of sending it immediately. They support the following business goals:

* **Prepare envelopes before sending.** Create an envelope and save it while its documents, recipients, fields, or metadata are still being prepared.
* **Update business data.** Replace the envelope XML or metadata when information changes before the envelope is sent.
* **Separate preparation from delivery.** Let one process create or update the envelope while another process validates and sends it.
* **Remove unused drafts.** Delete one or multiple draft envelopes that should no longer be completed or sent.
* **Preserve a stable identifier.** Save the returned envelope UUID and use it in subsequent update, delete, and send operations.

### Endpoint summary

<table><thead><tr><th width="99">Method</th><th width="165">Endpoint</th><th width="371">Purpose</th><th>Successful response</th></tr></thead><tbody><tr><td><code>POST</code></td><td><code>/api/v1/envelope</code></td><td>Create a draft envelope without sending it</td><td><code>201 Created</code></td></tr><tr><td><code>PUT</code></td><td><code>/api/v1/envelope</code></td><td>Update an existing draft envelope without sending it</td><td><code>200 OK</code></td></tr><tr><td><code>DELETE</code></td><td><code>/api/v1/envelope</code></td><td>Delete one or multiple draft envelopes</td><td><code>200 OK</code></td></tr></tbody></table>

### Draft envelope structure and headers

All three endpoints require an API token and the current mailbox UUID. The `Mailbox` header identifies the mailbox in which the draft is created, updated, or deleted.

```http
Authorization: Bearer {token}
Content-Type: application/json
Mailbox: {mailboxUuid}
```

The create and update endpoints use JSON request bodies containing envelope XML and optional metadata. The update request also supports previously saved document signature data.

<table><thead><tr><th width="181">Field</th><th width="82">Type</th><th width="93">Create</th><th width="95">Update</th><th>Description</th></tr></thead><tbody><tr><td><code>uuid</code></td><td>UUID</td><td>Optional</td><td>Required</td><td>UUID assigned to or identifying the draft envelope</td></tr><tr><td><code>data</code></td><td>string</td><td>Required</td><td>Required</td><td>Envelope XML serialized as a JSON string</td></tr><tr><td><code>metadata</code></td><td>object</td><td>Optional</td><td>Optional</td><td>Custom string key-value pairs stored with the envelope</td></tr><tr><td><code>savedDocSignatures</code></td><td>array</td><td>Not used</td><td>Optional</td><td>Previously saved document signature data included in the update</td></tr></tbody></table>

When `uuid` is omitted from the create request, DocStudio generates an envelope UUID and returns it in the response. When the integration assigns the UUID itself, include it in the create request and continue using the same value in later operations.

The `data` field must contain valid envelope XML referencing an existing template and template version. Serialize the XML as a JSON string and escape it when required. Role IDs, recipient mailbox UUIDs, document IDs, and field names must correspond to the selected template version. See [XML payloads](/guides/xml-payloads.md) for encoding guidance.

```xml
<envelope
  templateUuid="{templateUuid}"
  templateVersion="{templateVersion}"
>
  <info>
    <subject>Sales agreement</subject>
    <message>Please review and complete the agreement.</message>
  </info>

  <flow>
    <roles>
      <role
        id="{roleId}"
        mailboxUuid="{recipientMailboxUuid}"
      />
    </roles>
  </flow>

  <documents>
    <document id="{documentId}">
      <field name="Customer name">ACME Ltd</field>
      <field name="Contract amount">1250.50</field>
    </document>
  </documents>
</envelope>
```

The `metadata` object stores business identifiers and other custom values separately from the envelope XML. Metadata can later be used to connect the envelope with records in a CRM, ERP, or another external system.

```json
{
  "contractId": "CONTRACT-1042",
  "customerId": "CUSTOMER-77551",
  "source": "CRM"
}
```

### Practical example: Create, update, and delete a draft envelope

A CRM integration prepares a sales agreement but must wait for final customer information before sending it. The integration creates a draft, saves its UUID, updates the draft when the missing information becomes available, and deletes it if the agreement is cancelled.

#### **Create the draft envelope**

Create the draft with its initial envelope XML and metadata. The request saves the envelope in the mailbox specified by the `Mailbox` header without sending notifications or starting the recipient workflow.

{% tabs %}
{% tab title="Node.js" %}

```javascript
const payload = {
  data: "{envelopeXml}",
  metadata: {
    contractId: "CONTRACT-1042",
    customerId: "CUSTOMER-77551",
    source: "CRM"
  }
};

const response = await fetch(
  "https://api.docstudio.com/api/v1/envelope",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer {token}",
      Mailbox: "{mailboxUuid}",
      "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
  }
);

const body = await response.text();

if (!response.ok) {
  throw new Error(
    `Request failed: ${response.status}\n${body}`
  );
}

console.log(body);
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$payload = [
    "data" => "{envelopeXml}",
    "metadata" => [
        "contractId" => "CONTRACT-1042",
        "customerId" => "CUSTOMER-77551",
        "source" => "CRM"
    ]
];

$curl = curl_init(
    "https://api.docstudio.com/api/v1/envelope"
);

curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer {token}",
        "Mailbox: {mailboxUuid}",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true
]);

$body = curl_exec($curl);

if ($body === false) {
    throw new RuntimeException(curl_error($curl));
}

$status = curl_getinfo(
    $curl,
    CURLINFO_RESPONSE_CODE
);

curl_close($curl);

if ($status < 200 || $status >= 300) {
    throw new RuntimeException(
        "Request failed: {$status}\n{$body}"
    );
}

echo $body;
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

payload = {
    "data": "{envelopeXml}",
    "metadata": {
        "contractId": "CONTRACT-1042",
        "customerId": "CUSTOMER-77551",
        "source": "CRM",
    },
}

response = requests.post(
    "https://api.docstudio.com/api/v1/envelope",
    headers={
        "Authorization": "Bearer {token}",
        "Mailbox": "{mailboxUuid}",
    },
    json=payload,
    timeout=30,
)

response.raise_for_status()
print(response.json())
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require "json"
require "net/http"
require "uri"

uri = URI(
  "https://api.docstudio.com/api/v1/envelope"
)

payload = {
  data: "{envelopeXml}",
  metadata: {
    contractId: "CONTRACT-1042",
    customerId: "CUSTOMER-77551",
    source: "CRM"
  }
}

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer {token}"
request["Mailbox"] = "{mailboxUuid}"
request["Content-Type"] = "application/json"
request.body = JSON.generate(payload)

response = Net::HTTP.start(
  uri.hostname,
  uri.port,
  use_ssl: true
) do |http|
  http.request(request)
end

unless response.is_a?(Net::HTTPSuccess)
  raise "Request failed: #{response.code}\n#{response.body}"
end

puts response.body
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

func main() {
	payload := map[string]any{
		"data": "{envelopeXml}",
		"metadata": map[string]string{
			"contractId": "CONTRACT-1042",
			"customerId": "CUSTOMER-77551",
			"source":     "CRM",
		},
	}

	requestBody, err := json.Marshal(payload)
	if err != nil {
		panic(err)
	}

	request, err := http.NewRequest(
		http.MethodPost,
		"https://api.docstudio.com/api/v1/envelope",
		bytes.NewReader(requestBody),
	)
	if err != nil {
		panic(err)
	}

	request.Header.Set(
		"Authorization",
		"Bearer {token}",
	)
	request.Header.Set(
		"Mailbox",
		"{mailboxUuid}",
	)
	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 < 200 ||
		response.StatusCode >= 300 {
		panic(fmt.Sprintf(
			"Request failed: %s\n%s",
			response.Status,
			body,
		))
	}

	fmt.Println(string(body))
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
  public static void main(String[] args) throws Exception {
    String payload = """
        {
          "data": "{envelopeXml}",
          "metadata": {
            "contractId": "CONTRACT-1042",
            "customerId": "CUSTOMER-77551",
            "source": "CRM"
          }
        }
        """;

    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(
            "https://api.docstudio.com/api/v1/envelope"
        ))
        .header(
            "Authorization",
            "Bearer {token}"
        )
        .header(
            "Mailbox",
            "{mailboxUuid}"
        )
        .header(
            "Content-Type",
            "application/json"
        )
        .POST(
            HttpRequest.BodyPublishers.ofString(payload)
        )
        .build();

    HttpResponse<String> response =
        HttpClient.newHttpClient().send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

    if (response.statusCode() < 200 ||
        response.statusCode() >= 300) {
      throw new RuntimeException(
          "Request failed: " +
          response.statusCode() +
          "\n" +
          response.body()
      );
    }

    System.out.println(response.body());
  }
}
```

{% endtab %}

{% tab title=".NET" %}

```csharp
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

using var client = new HttpClient();

var payload = new
{
    data = "{envelopeXml}",
    metadata = new
    {
        contractId = "CONTRACT-1042",
        customerId = "CUSTOMER-77551",
        source = "CRM"
    }
};

using var request = new HttpRequestMessage(
    HttpMethod.Post,
    "https://api.docstudio.com/api/v1/envelope"
);

request.Headers.Authorization =
    new AuthenticationHeaderValue(
        "Bearer",
        "{token}"
    );

request.Headers.Add(
    "Mailbox",
    "{mailboxUuid}"
);

request.Content = new StringContent(
    JsonSerializer.Serialize(payload),
    Encoding.UTF8,
    "application/json"
);

using var response =
    await client.SendAsync(request);

var body =
    await response.Content.ReadAsStringAsync();

if (!response.IsSuccessStatusCode)
{
    throw new HttpRequestException(
        $"Request failed: " +
        $"{(int)response.StatusCode}\n{body}"
    );
}

Console.WriteLine(body);
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl --request POST \
  --url 'https://api.docstudio.com/api/v1/envelope' \
  --header 'Authorization: Bearer {token}' \
  --header 'Mailbox: {mailboxUuid}' \
  --header 'Content-Type: application/json' \
  --data '{
    "data": "{envelopeXml}",
    "metadata": {
      "contractId": "CONTRACT-1042",
      "customerId": "CUSTOMER-77551",
      "source": "CRM"
    }
  }'
```

{% endtab %}
{% endtabs %}

A successful request returns `201 Created` and the draft envelope UUID. Save this value because it identifies the draft in all subsequent operations.

```json
{
  "uuid": "{envelopeUuid}"
}
```

If the integration needs to assign the UUID before creating the draft, include it in the request. DocStudio then uses the supplied value instead of generating a new one.

```json
{
  "uuid": "{envelopeUuid}",
  "data": "{envelopeXml}",
  "metadata": {
    "contractId": "CONTRACT-1042",
    "source": "CRM"
  }
}
```

#### **Update the draft envelope**

Use `PUT /api/v1/envelope` after changing recipients, field values, the subject, the message, or other information stored in the envelope XML. Include the UUID returned by the create request and send the complete updated XML.

{% tabs %}
{% tab title="Node.js" %}

```javascript
const payload = {
  uuid: "{envelopeUuid}",
  data: "{updatedEnvelopeXml}",
  metadata: {
    contractId: "CONTRACT-1042",
    customerId: "CUSTOMER-77551",
    source: "CRM",
    draftState: "READY"
  }
};

const response = await fetch(
  "https://api.docstudio.com/api/v1/envelope",
  {
    method: "PUT",
    headers: {
      Authorization: "Bearer {token}",
      Mailbox: "{mailboxUuid}",
      "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
  }
);

const body = await response.text();

if (!response.ok) {
  throw new Error(
    `Request failed: ${response.status}\n${body}`
  );
}

if (body) {
  console.log(body);
}
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$payload = [
    "uuid" => "{envelopeUuid}",
    "data" => "{updatedEnvelopeXml}",
    "metadata" => [
        "contractId" => "CONTRACT-1042",
        "customerId" => "CUSTOMER-77551",
        "source" => "CRM",
        "draftState" => "READY"
    ]
];

$curl = curl_init(
    "https://api.docstudio.com/api/v1/envelope"
);

curl_setopt_array($curl, [
    CURLOPT_CUSTOMREQUEST => "PUT",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer {token}",
        "Mailbox: {mailboxUuid}",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true
]);

$body = curl_exec($curl);

if ($body === false) {
    throw new RuntimeException(curl_error($curl));
}

$status = curl_getinfo(
    $curl,
    CURLINFO_RESPONSE_CODE
);

curl_close($curl);

if ($status < 200 || $status >= 300) {
    throw new RuntimeException(
        "Request failed: {$status}\n{$body}"
    );
}

if ($body !== "") {
    echo $body;
}
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

payload = {
    "uuid": "{envelopeUuid}",
    "data": "{updatedEnvelopeXml}",
    "metadata": {
        "contractId": "CONTRACT-1042",
        "customerId": "CUSTOMER-77551",
        "source": "CRM",
        "draftState": "READY",
    },
}

response = requests.put(
    "https://api.docstudio.com/api/v1/envelope",
    headers={
        "Authorization": "Bearer {token}",
        "Mailbox": "{mailboxUuid}",
    },
    json=payload,
    timeout=30,
)

response.raise_for_status()

if response.text:
    print(response.text)
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require "json"
require "net/http"
require "uri"

uri = URI(
  "https://api.docstudio.com/api/v1/envelope"
)

payload = {
  uuid: "{envelopeUuid}",
  data: "{updatedEnvelopeXml}",
  metadata: {
    contractId: "CONTRACT-1042",
    customerId: "CUSTOMER-77551",
    source: "CRM",
    draftState: "READY"
  }
}

request = Net::HTTP::Put.new(uri)
request["Authorization"] = "Bearer {token}"
request["Mailbox"] = "{mailboxUuid}"
request["Content-Type"] = "application/json"
request.body = JSON.generate(payload)

response = Net::HTTP.start(
  uri.hostname,
  uri.port,
  use_ssl: true
) do |http|
  http.request(request)
end

unless response.is_a?(Net::HTTPSuccess)
  raise "Request failed: #{response.code}\n#{response.body}"
end

puts response.body unless response.body.empty?
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

func main() {
	payload := map[string]any{
		"uuid": "{envelopeUuid}",
		"data": "{updatedEnvelopeXml}",
		"metadata": map[string]string{
			"contractId":  "CONTRACT-1042",
			"customerId":  "CUSTOMER-77551",
			"source":      "CRM",
			"draftState":  "READY",
		},
	}

	requestBody, err := json.Marshal(payload)
	if err != nil {
		panic(err)
	}

	request, err := http.NewRequest(
		http.MethodPut,
		"https://api.docstudio.com/api/v1/envelope",
		bytes.NewReader(requestBody),
	)
	if err != nil {
		panic(err)
	}

	request.Header.Set(
		"Authorization",
		"Bearer {token}",
	)
	request.Header.Set(
		"Mailbox",
		"{mailboxUuid}",
	)
	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 < 200 ||
		response.StatusCode >= 300 {
		panic(fmt.Sprintf(
			"Request failed: %s\n%s",
			response.Status,
			body,
		))
	}

	if len(body) > 0 {
		fmt.Println(string(body))
	}
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
  public static void main(String[] args) throws Exception {
    String payload = """
        {
          "uuid": "{envelopeUuid}",
          "data": "{updatedEnvelopeXml}",
          "metadata": {
            "contractId": "CONTRACT-1042",
            "customerId": "CUSTOMER-77551",
            "source": "CRM",
            "draftState": "READY"
          }
        }
        """;

    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(
            "https://api.docstudio.com/api/v1/envelope"
        ))
        .header(
            "Authorization",
            "Bearer {token}"
        )
        .header(
            "Mailbox",
            "{mailboxUuid}"
        )
        .header(
            "Content-Type",
            "application/json"
        )
        .PUT(
            HttpRequest.BodyPublishers.ofString(payload)
        )
        .build();

    HttpResponse<String> response =
        HttpClient.newHttpClient().send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

    if (response.statusCode() < 200 ||
        response.statusCode() >= 300) {
      throw new RuntimeException(
          "Request failed: " +
          response.statusCode() +
          "\n" +
          response.body()
      );
    }

    if (!response.body().isEmpty()) {
      System.out.println(response.body());
    }
  }
}
```

{% endtab %}

{% tab title=".NET" %}

```csharp
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

using var client = new HttpClient();

var payload = new
{
    uuid = "{envelopeUuid}",
    data = "{updatedEnvelopeXml}",
    metadata = new
    {
        contractId = "CONTRACT-1042",
        customerId = "CUSTOMER-77551",
        source = "CRM",
        draftState = "READY"
    }
};

using var request = new HttpRequestMessage(
    HttpMethod.Put,
    "https://api.docstudio.com/api/v1/envelope"
);

request.Headers.Authorization =
    new AuthenticationHeaderValue(
        "Bearer",
        "{token}"
    );

request.Headers.Add(
    "Mailbox",
    "{mailboxUuid}"
);

request.Content = new StringContent(
    JsonSerializer.Serialize(payload),
    Encoding.UTF8,
    "application/json"
);

using var response =
    await client.SendAsync(request);

var body =
    await response.Content.ReadAsStringAsync();

if (!response.IsSuccessStatusCode)
{
    throw new HttpRequestException(
        $"Request failed: " +
        $"{(int)response.StatusCode}\n{body}"
    );
}

if (!string.IsNullOrEmpty(body))
{
    Console.WriteLine(body);
}
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl --request PUT \
  --url 'https://api.docstudio.com/api/v1/envelope' \
  --header 'Authorization: Bearer {token}' \
  --header 'Mailbox: {mailboxUuid}' \
  --header 'Content-Type: application/json' \
  --data '{
    "uuid": "{envelopeUuid}",
    "data": "{updatedEnvelopeXml}",
    "metadata": {
      "contractId": "CONTRACT-1042",
      "customerId": "CUSTOMER-77551",
      "source": "CRM",
      "draftState": "READY"
    }
  }'
```

{% endtab %}
{% endtabs %}

A successful update returns `200 OK`. The operation saves the new draft state but does not send the envelope or notify its recipients.

Use the same `Mailbox` header that provides access to the draft. Include the draft UUID and the updated envelope data in the request. The request must contain the complete envelope state that needs to remain after the update.

#### Include saved document signatures

The update request can include `savedDocSignatures` when saved signature data must be retained with the draft. Use it when updating an envelope that already contains signatures saved before it is sent. Each entry identifies the document, signature field, signature source, encoded signature data, and optional certificate data.

```json
{
  "uuid": "{envelopeUuid}",
  "data": "{updatedEnvelopeXml}",
  "metadata": {
    "contractId": "CONTRACT-1042"
  },
  "savedDocSignatures": [
    {
      "documentId": "{documentId}",
      "signatureName": "{signatureName}",
      "signatures": [
        {
          "source": "binary",
          "data": "{base64SignatureData}",
          "certificate": "{base64CertificateData}"
        }
      ]
    }
  ]
}
```

The `signatures` array must contain at least one signature entry. Omit `savedDocSignatures` when the draft does not contain saved document signatures.

#### Delete draft envelopes

Delete drafts that should no longer be completed or sent. The request accepts an array, so the integration can delete one or multiple drafts in the same operation.

{% tabs %}
{% tab title="Node.js" %}

```javascript
const payload = {
  uuids: [
    "{envelopeUuid}"
  ]
};

const response = await fetch(
  "https://api.docstudio.com/api/v1/envelope",
  {
    method: "DELETE",
    headers: {
      Authorization: "Bearer {token}",
      Mailbox: "{mailboxUuid}",
      "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
  }
);

const body = await response.text();

if (!response.ok) {
  throw new Error(
    `Request failed: ${response.status}\n${body}`
  );
}

if (body) {
  console.log(body);
}
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$payload = [
    "uuids" => [
        "{envelopeUuid}"
    ]
];

$curl = curl_init(
    "https://api.docstudio.com/api/v1/envelope"
);

curl_setopt_array($curl, [
    CURLOPT_CUSTOMREQUEST => "DELETE",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer {token}",
        "Mailbox: {mailboxUuid}",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true
]);

$body = curl_exec($curl);

if ($body === false) {
    throw new RuntimeException(curl_error($curl));
}

$status = curl_getinfo(
    $curl,
    CURLINFO_RESPONSE_CODE
);

curl_close($curl);

if ($status < 200 || $status >= 300) {
    throw new RuntimeException(
        "Request failed: {$status}\n{$body}"
    );
}

if ($body !== "") {
    echo $body;
}
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

response = requests.delete(
    "https://api.docstudio.com/api/v1/envelope",
    headers={
        "Authorization": "Bearer {token}",
        "Mailbox": "{mailboxUuid}",
    },
    json={
        "uuids": [
            "{envelopeUuid}",
        ],
    },
    timeout=30,
)

response.raise_for_status()

if response.text:
    print(response.text)
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require "json"
require "net/http"
require "uri"

uri = URI(
  "https://api.docstudio.com/api/v1/envelope"
)

payload = {
  uuids: [
    "{envelopeUuid}"
  ]
}

request = Net::HTTP::Delete.new(uri)
request["Authorization"] = "Bearer {token}"
request["Mailbox"] = "{mailboxUuid}"
request["Content-Type"] = "application/json"
request.body = JSON.generate(payload)

response = Net::HTTP.start(
  uri.hostname,
  uri.port,
  use_ssl: true
) do |http|
  http.request(request)
end

unless response.is_a?(Net::HTTPSuccess)
  raise "Request failed: #{response.code}\n#{response.body}"
end

puts response.body unless response.body.empty?
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

func main() {
	payload := map[string][]string{
		"uuids": {
			"{envelopeUuid}",
		},
	}

	requestBody, err := json.Marshal(payload)
	if err != nil {
		panic(err)
	}

	request, err := http.NewRequest(
		http.MethodDelete,
		"https://api.docstudio.com/api/v1/envelope",
		bytes.NewReader(requestBody),
	)
	if err != nil {
		panic(err)
	}

	request.Header.Set(
		"Authorization",
		"Bearer {token}",
	)
	request.Header.Set(
		"Mailbox",
		"{mailboxUuid}",
	)
	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 < 200 ||
		response.StatusCode >= 300 {
		panic(fmt.Sprintf(
			"Request failed: %s\n%s",
			response.Status,
			body,
		))
	}

	if len(body) > 0 {
		fmt.Println(string(body))
	}
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
  public static void main(String[] args) throws Exception {
    String payload = """
        {
          "uuids": [
            "{envelopeUuid}"
          ]
        }
        """;

    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(
            "https://api.docstudio.com/api/v1/envelope"
        ))
        .header(
            "Authorization",
            "Bearer {token}"
        )
        .header(
            "Mailbox",
            "{mailboxUuid}"
        )
        .header(
            "Content-Type",
            "application/json"
        )
        .method(
            "DELETE",
            HttpRequest.BodyPublishers.ofString(payload)
        )
        .build();

    HttpResponse<String> response =
        HttpClient.newHttpClient().send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

    if (response.statusCode() < 200 ||
        response.statusCode() >= 300) {
      throw new RuntimeException(
          "Request failed: " +
          response.statusCode() +
          "\n" +
          response.body()
      );
    }

    if (!response.body().isEmpty()) {
      System.out.println(response.body());
    }
  }
}
```

{% endtab %}

{% tab title=".NET" %}

```csharp
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

using var client = new HttpClient();

var payload = new
{
    uuids = new[]
    {
        "{envelopeUuid}"
    }
};

using var request = new HttpRequestMessage(
    HttpMethod.Delete,
    "https://api.docstudio.com/api/v1/envelope"
);

request.Headers.Authorization =
    new AuthenticationHeaderValue(
        "Bearer",
        "{token}"
    );

request.Headers.Add(
    "Mailbox",
    "{mailboxUuid}"
);

request.Content = new StringContent(
    JsonSerializer.Serialize(payload),
    Encoding.UTF8,
    "application/json"
);

using var response =
    await client.SendAsync(request);

var body =
    await response.Content.ReadAsStringAsync();

if (!response.IsSuccessStatusCode)
{
    throw new HttpRequestException(
        $"Request failed: " +
        $"{(int)response.StatusCode}\n{body}"
    );
}

if (!string.IsNullOrEmpty(body))
{
    Console.WriteLine(body);
}
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl --request DELETE \
  --url 'https://api.docstudio.com/api/v1/envelope' \
  --header 'Authorization: Bearer {token}' \
  --header 'Mailbox: {mailboxUuid}' \
  --header 'Content-Type: application/json' \
  --data '{
    "uuids": [
      "{envelopeUuid}"
    ]
  }'
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Review every UUID before sending a delete request. The operation can delete multiple drafts in one request.
{% endhint %}

To delete multiple drafts, include each envelope UUID in the same array. All specified envelopes must be accessible through the mailbox context used in the request.

```json
{
  "uuids": [
    "{firstEnvelopeUuid}",
    "{secondEnvelopeUuid}",
    "{thirdEnvelopeUuid}"
  ]
}
```

A successful request returns `200 OK`. This endpoint is intended for draft envelopes that have not entered the recipient workflow.

#### Send the completed draft

When the draft is ready, send it through `POST /api/v1/envelope/send`. Include the saved envelope UUID and the final envelope XML so DocStudio can start the configured workflow.

{% tabs %}
{% tab title="Node.js" %}

```javascript
const payload = {
  uuid: "{envelopeUuid}",
  data: "{finalEnvelopeXml}",
  metadata: {
    contractId: "CONTRACT-1042",
    customerId: "CUSTOMER-77551",
    source: "CRM"
  }
};

const response = await fetch(
  "https://api.docstudio.com/api/v1/envelope/send",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer {token}",
      Mailbox: "{mailboxUuid}",
      "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
  }
);

const body = await response.text();

if (!response.ok) {
  throw new Error(
    `Request failed: ${response.status}\n${body}`
  );
}

if (body) {
  console.log(body);
}
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$payload = [
    "uuid" => "{envelopeUuid}",
    "data" => "{finalEnvelopeXml}",
    "metadata" => [
        "contractId" => "CONTRACT-1042",
        "customerId" => "CUSTOMER-77551",
        "source" => "CRM"
    ]
];

$curl = curl_init(
    "https://api.docstudio.com/api/v1/envelope/send"
);

curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer {token}",
        "Mailbox: {mailboxUuid}",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true
]);

$body = curl_exec($curl);

if ($body === false) {
    throw new RuntimeException(curl_error($curl));
}

$status = curl_getinfo(
    $curl,
    CURLINFO_RESPONSE_CODE
);

curl_close($curl);

if ($status < 200 || $status >= 300) {
    throw new RuntimeException(
        "Request failed: {$status}\n{$body}"
    );
}

if ($body !== "") {
    echo $body;
}
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

payload = {
    "uuid": "{envelopeUuid}",
    "data": "{finalEnvelopeXml}",
    "metadata": {
        "contractId": "CONTRACT-1042",
        "customerId": "CUSTOMER-77551",
        "source": "CRM",
    },
}

response = requests.post(
    "https://api.docstudio.com/api/v1/envelope/send",
    headers={
        "Authorization": "Bearer {token}",
        "Mailbox": "{mailboxUuid}",
    },
    json=payload,
    timeout=30,
)

response.raise_for_status()

if response.text:
    print(response.text)
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require "json"
require "net/http"
require "uri"

uri = URI(
  "https://api.docstudio.com/api/v1/envelope/send"
)

payload = {
  uuid: "{envelopeUuid}",
  data: "{finalEnvelopeXml}",
  metadata: {
    contractId: "CONTRACT-1042",
    customerId: "CUSTOMER-77551",
    source: "CRM"
  }
}

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer {token}"
request["Mailbox"] = "{mailboxUuid}"
request["Content-Type"] = "application/json"
request.body = JSON.generate(payload)

response = Net::HTTP.start(
  uri.hostname,
  uri.port,
  use_ssl: true
) do |http|
  http.request(request)
end

unless response.is_a?(Net::HTTPSuccess)
  raise "Request failed: #{response.code}\n#{response.body}"
end

puts response.body unless response.body.empty?
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

func main() {
	payload := map[string]any{
		"uuid": "{envelopeUuid}",
		"data": "{finalEnvelopeXml}",
		"metadata": map[string]string{
			"contractId": "CONTRACT-1042",
			"customerId": "CUSTOMER-77551",
			"source":     "CRM",
		},
	}

	requestBody, err := json.Marshal(payload)
	if err != nil {
		panic(err)
	}

	request, err := http.NewRequest(
		http.MethodPost,
		"https://api.docstudio.com/api/v1/envelope/send",
		bytes.NewReader(requestBody),
	)
	if err != nil {
		panic(err)
	}

	request.Header.Set(
		"Authorization",
		"Bearer {token}",
	)
	request.Header.Set(
		"Mailbox",
		"{mailboxUuid}",
	)
	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 < 200 ||
		response.StatusCode >= 300 {
		panic(fmt.Sprintf(
			"Request failed: %s\n%s",
			response.Status,
			body,
		))
	}

	if len(body) > 0 {
		fmt.Println(string(body))
	}
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
  public static void main(String[] args) throws Exception {
    String payload = """
        {
          "uuid": "{envelopeUuid}",
          "data": "{finalEnvelopeXml}",
          "metadata": {
            "contractId": "CONTRACT-1042",
            "customerId": "CUSTOMER-77551",
            "source": "CRM"
          }
        }
        """;

    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(
            "https://api.docstudio.com/api/v1/envelope/send"
        ))
        .header(
            "Authorization",
            "Bearer {token}"
        )
        .header(
            "Mailbox",
            "{mailboxUuid}"
        )
        .header(
            "Content-Type",
            "application/json"
        )
        .POST(
            HttpRequest.BodyPublishers.ofString(payload)
        )
        .build();

    HttpResponse<String> response =
        HttpClient.newHttpClient().send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

    if (response.statusCode() < 200 ||
        response.statusCode() >= 300) {
      throw new RuntimeException(
          "Request failed: " +
          response.statusCode() +
          "\n" +
          response.body()
      );
    }

    if (!response.body().isEmpty()) {
      System.out.println(response.body());
    }
  }
}
```

{% endtab %}

{% tab title=".NET" %}

```csharp
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

using var client = new HttpClient();

var payload = new
{
    uuid = "{envelopeUuid}",
    data = "{finalEnvelopeXml}",
    metadata = new
    {
        contractId = "CONTRACT-1042",
        customerId = "CUSTOMER-77551",
        source = "CRM"
    }
};

using var request = new HttpRequestMessage(
    HttpMethod.Post,
    "https://api.docstudio.com/api/v1/envelope/send"
);

request.Headers.Authorization =
    new AuthenticationHeaderValue(
        "Bearer",
        "{token}"
    );

request.Headers.Add(
    "Mailbox",
    "{mailboxUuid}"
);

request.Content = new StringContent(
    JsonSerializer.Serialize(payload),
    Encoding.UTF8,
    "application/json"
);

using var response =
    await client.SendAsync(request);

var body =
    await response.Content.ReadAsStringAsync();

if (!response.IsSuccessStatusCode)
{
    throw new HttpRequestException(
        $"Request failed: " +
        $"{(int)response.StatusCode}\n{body}"
    );
}

if (!string.IsNullOrEmpty(body))
{
    Console.WriteLine(body);
}
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl --request POST \
  --url 'https://api.docstudio.com/api/v1/envelope/send' \
  --header 'Authorization: Bearer {token}' \
  --header 'Mailbox: {mailboxUuid}' \
  --header 'Content-Type: application/json' \
  --data '{
    "uuid": "{envelopeUuid}",
    "data": "{finalEnvelopeXml}",
    "metadata": {
      "contractId": "CONTRACT-1042",
      "customerId": "CUSTOMER-77551",
      "source": "CRM"
    }
  }'
```

{% endtab %}
{% endtabs %}

A successful request returns `201 Created`. The envelope is no longer only a draft because DocStudio starts the recipient workflow defined by its template and envelope XML.

### Troubleshooting

<table><thead><tr><th width="272.111083984375">Problem</th><th>What to check</th></tr></thead><tbody><tr><td>The request XML is rejected</td><td>Verify that the XML references the selected template version and contains valid roles, recipients, documents, and fields</td></tr><tr><td>The draft is unavailable</td><td>Confirm that the <code>Mailbox</code> header grants access to the envelope UUID</td></tr><tr><td>An update lost expected values</td><td>Retrieve the current draft before using <code>PUT</code>, then submit the complete envelope state</td></tr></tbody></table>

### Related topics

{% columns %}
{% column %}

* [Send Envelope](/api-reference/send-envelope.md)
* [Envelope Structure](/guides/envelope-structure.md)
* [Templates and reusable IDs](/guides/templates-and-reusable-ids.md)
* [Envelope metadata](/guides/envelope-metadata.md)
  {% endcolumn %}

{% column %}

* [Upload Attachments](/api-reference/upload-attachments.md)
* [Check Envelope Status](/api-reference/check-envelope-status.md)
* [Error handling and API error codes](/error-codes/error-handling-and-api-error-codes.md)
  {% endcolumn %}
  {% endcolumns %}
