Save an existing R2 object as a contact document
curl --request POST \
--url https://your-instance.example.com/api/contacts/{contactId}/documents \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"storageKey": "org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf",
"filename": "id-proof.pdf",
"contentType": "application/pdf",
"size": 184320,
"documentType": "ID Proof",
"notes": "Driver license, front + back"
}
'import requests
url = "https://your-instance.example.com/api/contacts/{contactId}/documents"
payload = {
"storageKey": "org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf",
"filename": "id-proof.pdf",
"contentType": "application/pdf",
"size": 184320,
"documentType": "ID Proof",
"notes": "Driver license, front + back"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
storageKey: 'org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf',
filename: 'id-proof.pdf',
contentType: 'application/pdf',
size: 184320,
documentType: 'ID Proof',
notes: 'Driver license, front + back'
})
};
fetch('https://your-instance.example.com/api/contacts/{contactId}/documents', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://your-instance.example.com/api/contacts/{contactId}/documents",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'storageKey' => 'org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf',
'filename' => 'id-proof.pdf',
'contentType' => 'application/pdf',
'size' => 184320,
'documentType' => 'ID Proof',
'notes' => 'Driver license, front + back'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://your-instance.example.com/api/contacts/{contactId}/documents"
payload := strings.NewReader("{\n \"storageKey\": \"org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf\",\n \"filename\": \"id-proof.pdf\",\n \"contentType\": \"application/pdf\",\n \"size\": 184320,\n \"documentType\": \"ID Proof\",\n \"notes\": \"Driver license, front + back\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://your-instance.example.com/api/contacts/{contactId}/documents")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"storageKey\": \"org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf\",\n \"filename\": \"id-proof.pdf\",\n \"contentType\": \"application/pdf\",\n \"size\": 184320,\n \"documentType\": \"ID Proof\",\n \"notes\": \"Driver license, front + back\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://your-instance.example.com/api/contacts/{contactId}/documents")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"storageKey\": \"org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf\",\n \"filename\": \"id-proof.pdf\",\n \"contentType\": \"application/pdf\",\n \"size\": 184320,\n \"documentType\": \"ID Proof\",\n \"notes\": \"Driver license, front + back\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"document": {
"_id": "64f0a1b2c3d4e5f6a7b8ca40",
"storageKey": "org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf",
"filename": "id-proof.pdf",
"contentType": "application/pdf",
"documentType": "ID Proof"
}
}{
"error": "Attachment not found",
"message": "Attachment not found"
}{
"error": "Attachment not found",
"message": "Attachment not found"
}{
"error": "Attachment not found",
"message": "Attachment not found"
}{
"error": "Attachment not found",
"message": "Attachment not found"
}{
"error": "Attachment not found",
"message": "Attachment not found"
}Contacts
Save an existing R2 object as a contact document
Attaches an already-uploaded R2 object (referenced by storageKey) to the contact as a typed document. Validates documentType against the organization’s configured documentTypes.
POST
/
api
/
contacts
/
{contactId}
/
documents
Save an existing R2 object as a contact document
curl --request POST \
--url https://your-instance.example.com/api/contacts/{contactId}/documents \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"storageKey": "org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf",
"filename": "id-proof.pdf",
"contentType": "application/pdf",
"size": 184320,
"documentType": "ID Proof",
"notes": "Driver license, front + back"
}
'import requests
url = "https://your-instance.example.com/api/contacts/{contactId}/documents"
payload = {
"storageKey": "org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf",
"filename": "id-proof.pdf",
"contentType": "application/pdf",
"size": 184320,
"documentType": "ID Proof",
"notes": "Driver license, front + back"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
storageKey: 'org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf',
filename: 'id-proof.pdf',
contentType: 'application/pdf',
size: 184320,
documentType: 'ID Proof',
notes: 'Driver license, front + back'
})
};
fetch('https://your-instance.example.com/api/contacts/{contactId}/documents', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://your-instance.example.com/api/contacts/{contactId}/documents",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'storageKey' => 'org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf',
'filename' => 'id-proof.pdf',
'contentType' => 'application/pdf',
'size' => 184320,
'documentType' => 'ID Proof',
'notes' => 'Driver license, front + back'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://your-instance.example.com/api/contacts/{contactId}/documents"
payload := strings.NewReader("{\n \"storageKey\": \"org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf\",\n \"filename\": \"id-proof.pdf\",\n \"contentType\": \"application/pdf\",\n \"size\": 184320,\n \"documentType\": \"ID Proof\",\n \"notes\": \"Driver license, front + back\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://your-instance.example.com/api/contacts/{contactId}/documents")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"storageKey\": \"org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf\",\n \"filename\": \"id-proof.pdf\",\n \"contentType\": \"application/pdf\",\n \"size\": 184320,\n \"documentType\": \"ID Proof\",\n \"notes\": \"Driver license, front + back\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://your-instance.example.com/api/contacts/{contactId}/documents")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"storageKey\": \"org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf\",\n \"filename\": \"id-proof.pdf\",\n \"contentType\": \"application/pdf\",\n \"size\": 184320,\n \"documentType\": \"ID Proof\",\n \"notes\": \"Driver license, front + back\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"document": {
"_id": "64f0a1b2c3d4e5f6a7b8ca40",
"storageKey": "org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf",
"filename": "id-proof.pdf",
"contentType": "application/pdf",
"documentType": "ID Proof"
}
}{
"error": "Attachment not found",
"message": "Attachment not found"
}{
"error": "Attachment not found",
"message": "Attachment not found"
}{
"error": "Attachment not found",
"message": "Attachment not found"
}{
"error": "Attachment not found",
"message": "Attachment not found"
}{
"error": "Attachment not found",
"message": "Attachment not found"
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Body
application/json
R2 storage key of the already-uploaded object.
Must match a name in the organization's configured documentTypes.
File size in bytes. If missing or 0, the server will attempt to look it up from R2.
Optional originating message id when the document was promoted from an attachment.
Response
Document attached
Available options:
true Show child attributes
Show child attributes
Example:
{
"_id": "64f0a1b2c3d4e5f6a7b8ca40",
"storageKey": "org/64f0a1b2c3d4e5f6a7b8c9d3/contact/64f0a1b2c3d4e5f6a7b8c9d0/2026/id-proof.pdf",
"filename": "id-proof.pdf",
"contentType": "application/pdf",
"size": 184320,
"documentType": "ID Proof",
"uploadedAt": "2026-05-12T10:00:00.000Z",
"uploadedBy": "64f0a1b2c3d4e5f6a7b8c9d4",
"notes": "Driver license, front + back",
"signedUrl": "https://example-r2.signed/.../id-proof.pdf?X-Amz-Expires=600"
}
⌘I