curl --request POST \
--url https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents \
--header 'Authorization: <api-key>' \
--header 'Content-Type: multipart/form-data' \
--header 'Idempotency-Key: <idempotency-key>' \
--form file=@example-fileconst form = new FormData();
form.append('file', '<string>');
const options = {
method: 'POST',
headers: {'Idempotency-Key': '<idempotency-key>', Authorization: '<api-key>'}
};
options.body = form;
fetch('https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import fs from 'fs';
const formData = new FormData();
formData.append('file', await new Response(fs.createReadStream('example-file')).blob());
const url = 'https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents';
const options = {
method: 'POST',
headers: {'Idempotency-Key': '<idempotency-key>', Authorization: '<api-key>'},
body: formData
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));import requests
url = "https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents"
files = { "file": ("example-file", open("example-file", "rb")) }
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "<api-key>"
}
response = requests.post(url, files=files, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("Authorization", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/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 => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Idempotency-Key: <idempotency-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"id": "<string>",
"companyId": "<string>",
"name": "Articles-of-Organization.pdf",
"contentType": "application/pdf",
"documentType": "ArticlesOfOrganization",
"lastModified": "2026-07-06T21:11:56Z",
"createdAt": "2026-07-06T21:11:56Z"
}Upload a company document
Adds one document to a company you own, sent as multipart/form-data with the file in file and its type in documentType. The file must be a PDF of at most 10MB, whatever the document type, with a name of at most 255 characters; a PDF is recognised from the file’s contents, not from the declared content type. The stored document is returned and from then on appears in the list endpoint and can be downloaded by its id. Uploading a document has the same effect as doola receiving it: an ArticlesOfOrganization or EinLetter upload starts the work that document unlocks, and the matching document_*_uploaded webhook is sent for every type except OTHER. Idempotent on the Idempotency-Key header: retrying the same file with the same key returns the first upload instead of storing a second copy.
curl --request POST \
--url https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents \
--header 'Authorization: <api-key>' \
--header 'Content-Type: multipart/form-data' \
--header 'Idempotency-Key: <idempotency-key>' \
--form file=@example-fileconst form = new FormData();
form.append('file', '<string>');
const options = {
method: 'POST',
headers: {'Idempotency-Key': '<idempotency-key>', Authorization: '<api-key>'}
};
options.body = form;
fetch('https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import fs from 'fs';
const formData = new FormData();
formData.append('file', await new Response(fs.createReadStream('example-file')).blob());
const url = 'https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents';
const options = {
method: 'POST',
headers: {'Idempotency-Key': '<idempotency-key>', Authorization: '<api-key>'},
body: formData
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));import requests
url = "https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents"
files = { "file": ("example-file", open("example-file", "rb")) }
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "<api-key>"
}
response = requests.post(url, files=files, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("Authorization", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/documents")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.test.doola.com/v1/partner/companies/{doolaCompanyId}/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 => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Idempotency-Key: <idempotency-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"id": "<string>",
"companyId": "<string>",
"name": "Articles-of-Organization.pdf",
"contentType": "application/pdf",
"documentType": "ArticlesOfOrganization",
"lastModified": "2026-07-06T21:11:56Z",
"createdAt": "2026-07-06T21:11:56Z"
}Authorizations
Partner API key. Send the raw key as the Authorization header value — e.g. dk_test_… in sandbox or dk_live_… in production. Generate and rotate keys in the doola Partner Portal.
Headers
Unique key that makes the upload safe to retry. Reuse the same value to retry; if the upload fails before it is stored, the key is released, so retry with the same value.
Path Parameters
doola company ID (KSUID).
Body
What the document is. ArticlesOfOrganization is the formation document for both entity types: the Articles of Organization of an LLC, and the Articles or Certificate of Incorporation of a C-Corp. EinLetter is the IRS CP 575 or 147C letter; OTHER is any other company document.
ArticlesOfOrganization, EinLetter, OperatingAgreement, CorporateBylaws, OTHER The document: a PDF of at most 10MB.
Response
Created
Metadata for a company document.
Document ID.
doola company ID (KSUID) the document belongs to.
File name.
"Articles-of-Organization.pdf"
MIME content type.
"application/pdf"
Document type — for example ArticlesOfOrganization, EinLetter, Mail, SignedSS4 or SignedForm8821. May be absent for a file whose source provides no type.
"ArticlesOfOrganization"
Last modified timestamp (RFC 3339).
"2026-07-06T21:11:56Z"
Creation timestamp (RFC 3339). May be null for files whose source provides no creation time.
"2026-07-06T21:11:56Z"