Introduction
Welcome to the PaperOS API!
You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.
This example API documentation page was created with DocuAPI, a multilingual documentation theme for the static site generator Hugo Extended Edition.
Prerequisites
Install the tools used in the examples
curl https://webi.sh/jq@1 | sh
curl https://webi.sh/node@lts | sh
source ~/.local/envman/PATH.sh
Please install jq and node to follow along in the examples.
API Base URL
The Base URL will be in the form of
https://app.paperos.com
https://example.c.paperos.com
https://paperos.example.com
https://demo.example.c.paperos.net
Use
example.c.paperos.dev(note:.dev) for the live examples:
export PAPEROS_BASE_URL='https://example.c.paperos.dev'
var paperBase = process.env.PAPEROS_BASE_URL;
Depending on your account, your PaperOS base URL may resemble any of the following:
For general operations you can always use https://app.paperos.com, however, using the branded domain may be required for certain actions, such as those that generate branded notifications.
Your Sandbox base URL may look something like https://demo.example.c.paperos.net.
To-Dos
There are multiple things that need to have public-facing IDs and slugs and name changes:
- Needs truncated responses
- tasks, transactions
- lots of things
- Needs Segmentation
- Individual
- Needs Slugs (or Enums)
role_id(maybe?)investor_documents_list("All of the above", etc)upload_or_generate("Generate", etc)
OIDC Client
Our goal is Just Works™ compatibility with OIDC clients for:
- OIDC Section 2: ID Token
- OIDC Section 3.1: Authorization Code Flow
- OIDC Section 3.2: Implicit Flow
- OIDC Section 5.1: Standard Claims
- OIDC Dynamic Client Registration 1.0 incorporating errata set 2
However, as the spec is large and many pieces are sections have been superseded by other methodologies or unadopted in industry, we implement functionality as needed.
Please report any incompatibility with compliant clients to support as a bug.
Registration
In general, you should contact support to create and configure your OIDC Client.
Although you can create your own client with your user Access Token, it will have minimal permissions and may not be useful.
POST /api/v1/oidc/clients
curl --fail-with-body -X POST "${PAPEROS_BASE_URL}/api/v1/oidc/clients" \
-H "Authorization: Bearer ${UI_ACCESS_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{
"redirect_uris": ["https://app.example.com/callback", "https://app.example.com/return"],
"response_types": ["code"],
"grant_types": ["authorization_code"],
"contacts": ["devops@example.com"],
"client_name": "Example App",
"logo_uri": "https://app.example.com/logo.png",
"client_uri": "https://app.example.com",
"policy_uri": "https://app.example.com/privacy",
"tos_uri": "https://app.example.com/terms",
"sector_identifier_uri": "https://example.com/allowed_redirects.json",
"initiate_login_uri": "https://app.example.com/oidc/start"
}'
var data = {
redirect_uris: [
"https://app.example.com/callback",
"https://app.example.com/return",
],
response_types: ["code"],
grant_types: ["authorization_code"],
contacts: ["devops@example.com"],
client_name: "Example App",
logo_uri: "https://app.example.com/logo.png",
client_uri: "https://app.example.com",
policy_uri: "https://app.example.com/privacy",
tos_uri: "https://app.example.com/terms",
sector_identifier_uri: "https://example.com/allowed_redirects.json",
initiate_login_uri: "https://app.example.com/oidc/start",
};
var payload = JSON.stringify(data, null, 2);
var url = `${paperBase}/api/v1/oidc/clients`;
var resp = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${uiAccessToken}`,
"Content-Type": "application/json",
},
body: payload,
});
var oidcClient = await resp.json();
console.log(oidcClient);
Example Response:
{
"rp_id": "ocrp_zdcw9paqtecqby8c",
"sector_identifier": "a468aa0d-97ff-4b92-849e-63fe7f6b1817",
"redirect_uris": [
"https://app.example.com/callback",
"https://app.example.com/return"
],
"response_types": ["code"],
"grant_types": ["authorization_code"],
"application_type": "web",
"contacts": ["devops@example.com"],
"client_name": "Example App",
"logo_uri": "https://app.example.com/logo.png",
"client_uri": "https://app.example.com",
"policy_uri": "https://app.example.com/privacy",
"tos_uri": "https://app.example.com/terms",
"jwks_uri": null,
"subject_type": "pairwise",
"token_endpoint_auth_method": "client_secret_basic",
"default_max_age": 0,
"require_auth_time": true,
"initiate_login_uri": "https://app.example.com/oidc/start",
"requestable_scopes": ["profile"],
"client_secret": "86fa1382-055c-492d-9f0b-7fc0963ca0c7",
"client_secret_expires_at": 0,
"client_id": "oidc_pc2tsa5rd1m4tamb",
"client_id_issued_at": 1745559634
}
Caveats:
- typically, you should not create multiple clients, as they will have different pairwise identifiers
- as we rely exclusively on HTTPS for encryption, we do not support many of the optional JWE- and JWS-related options
For more information on the meaning of the various fields, and additional available fields, see the Client Metadata glossary:
List
GET /api/v1/oidc/clients
curl --fail-with-body "${PAPEROS_BASE_URL}/api/v1/oidc/clients" \
-H "Authorization: Bearer ${UI_ACCESS_TOKEN}"
var url = `${paperBase}/api/v1/oidc/clients`;
var resp = await fetch(url, {
headers: { Authorization: `Bearer ${uiAccessToken}` },
});
var oidcClients = await resp.json();
console.log(oidcClients);
Example Response:
{
"success": true,
"total": 1,
"type": "[]<oidc_client>",
"oidc_clients": [
{
"client_id": "oidc_zdcw93k7h4w4zb2m",
"rp_id": "ocrp_zdcw9paqtecqby8c",
"org_id": "org_01he3t2sjcs6zybd2cemyvn7qn",
"external_id": null,
"sector_identifier": "15e4cd2d-22e8-4f47-b9ca-8b80d7b4a870",
"redirect_uris": [
"https://app.example.com/callback",
"https://app.example.com/return"
],
"response_types": ["code"],
"grant_types": ["authorization_code"],
"application_type": "web",
"contacts": ["devops@example.com"],
"client_name": "Example App",
"logo_uri": "https://app.example.com/logo.png",
"client_uri": "https://app.example.com",
"policy_uri": "https://app.example.com/privacy",
"tos_uri": "https://app.example.com/terms",
"jwks_uri": null,
"sector_identifier_uri": null,
"subject_type": "pairwise",
"token_endpoint_auth_method": "client_secret_basic",
"default_max_age": 0,
"require_auth_time": true,
"initiate_login_uri": "https://app.example.com/oidc/start",
"requestable_scopes": ["profile", "impersonation"],
"client_id_issued_at": 1745455434,
"secrets": [
{
"id": "occs_zdcw9631vkaf4ppk",
"secret": "9ba82b72-38eb-4a6d-bc57-bace7a0d5bd1",
"comment": "",
"expires_at": null,
"created_at": "2025-04-23T18:43:54Z",
"revoked_at": null
}
],
"client_secret_expires_at": 0,
"client_secret": "9ba82b72-38eb-4a6d-bc57-bace7a0d5bd1"
}
],
"count": 1
}
Extensions:
- multiple secrets: we support multiple secrets to allow for a grace period during rotation
Create Subject (User)
POST /api/v1/integrations/users
curl -v --fail-with-body -X POST "${PAPEROS_BASE_URL}/api/v1/integrations/users" \
--user "${CLIENT_ID}:${CLIENT_SECRET}" \
-H 'Content-Type: application/json' \
-d '{
"external_id": "john_doe-101",
"email": "john.doe+101@example.com",
"email_verified": false,
"given_name": "John",
"family_name": "Doe",
"zoneinfo": "America/Denver",
"locale": "en-US"
}' | jq
var intlData = Intl.DateTimeFormat().resolvedOptions();
var data = {
external_id: "john_doe-101",
email: "john.doe+101@example.com",
email_verified: false,
given_name: "John",
family_name: "Doe",
zoneinfo: intlData.timeZone,
locale: intlData.locale,
};
var payload = JSON.stringify(data, null, 2);
var url = `${PAPEROS_BASE_URL}/api/v1/integrations/users`;
var basicAuth = btoa(`${oidc.client_id}:${oidc.client_secret}`);
var resp = await fetch(url, {
method: "POST",
credentials: "include",
headers: {
Authorization: `Basic ${basicAuth}`,
"Content-Type": "application/json",
},
body: payload,
});
var subjectResp = await resp.json();
console.log(subjectResp);
Example Result:
{
"success": true,
"type": "<oidc_subject>",
"subject": {
"oidc_client_id": "oidc_zdcw93k7h4w4zb2m",
"sub": "sub_159rjy10v4qpfr0d",
"external_id": "john_doe-101",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe+101@example.com",
"phone": null,
"zoneinfo": "America/Denver",
"locale": "en-US",
"picture": null,
"granted_scopes": ["profile", "impersonation"],
"orgs": [],
"created_at": "2025-04-25 08:31:23Z",
"updated_at": "2025-04-25 08:31:23Z"
}
}
Update Subject (User)
Set the user's externalId as well as their current locale and zoneinfo.
This is primarily for the initial user sync.
PATCH /api/v1/integrations/users/:sub
b_ppid_sub='sub_xxxxxxxx'
b_our_id='100-john_doe'
curl -v --fail-with-body -X PATCH "${PAPEROS_BASE_URL}/api/v1/integrations/users/${b_ppid_sub}" \
--user "${CLIENT_ID}:${CLIENT_SECRET}" \
-H 'Content-Type: application/json' \
-d '{
"external_id": "'"${b_our_id}"'",
"locale": "en-US",
"zoneinfo": "America/Denver"
}' | jq
var ppidSub = "sub_xxxxxxxx";
var ourId = "100-john_doe";
var intlData = Intl.DateTimeFormat().resolvedOptions();
var data = {
external_id: ourId,
locale: intlData.locale,
zoneinfo: intlData.timeZone,
};
var payload = JSON.stringify(data, null, 2);
var url = `${PAPEROS_BASE_URL}/api/v1/integrations/users/${ppidSub}`;
var basicAuth = btoa(`${oidc.client_id}:${oidc.client_secret}`);
var resp = await fetch(url, {
method: "PATCH",
credentials: "include",
headers: {
Authorization: `Basic ${basicAuth}`,
"Content-Type": "application/json",
},
body: payload,
});
console.log("OK:", resp.status, resp.ok);
Example Result:
204 NO CONTENT
List Subjects (Users)
GET /api/v1/integrations/users
curl --fail-with-body "${PAPEROS_BASE_URL}/api/v1/integrations/users" \
--user "${CLIENT_ID}:${CLIENT_SECRET}" |
jq
var url = `${PAPEROS_BASE_URL}/api/v1/integrations/users`;
var basicAuth = btoa(`${oidc.client_id}:${oidc.client_secret}`);
var resp = await fetch(url, {
credentials: "include",
headers: { Authorization: `Basic ${basicAuth}` },
});
Example Response:
{
"success": true,
"type": "<[]oidc_subject>",
"subjects": [
{
"given_name": "John",
"family_name": "Doe",
"locale": "en-US",
"zoneinfo": "America/Denver",
"external_id": "john_doe-101",
"sub": "sub_d0255a8r3hknnzsh",
"granted_scopes": ["profile", "impersonation"],
"orgs": [
{
"id": "org_ew421jr4pmnhs02z",
"name": "My For Profit OIDC Client Test Co"
}
]
}
]
}
Authentication
Access Tokens can be created with your OIDC ID and Secret.
Set Organization-Scoped Access Token
export OIDC_ACCESS_TOKEN='xxxx.yyyy.zzzz'
var paperToken = process.env.OIDC_ACCESS_TOKEN;
PaperOS uses API keys to allow access to the API.
All API requests should include the API token in the Authorization header with the Bearer prefix:
Authorization: Bearer <token>
You can register a new PaperOS API key at our developer portal.
Create ID Token
The ID Token represents the subject (user) and can be used to create an organization.
POST /api/v1/integrations/id-token
curl -v --fail-with-body -X POST "${PAPEROS_BASE_URL}/api/v1/integrations/id-token" \
--user "${CLIENT_ID}:${CLIENT_SECRET}" \
-H "Content-Type: application/json" \
-d '{
"claims": {
"external_id": { "value": "john_doe-101" },
"auth_time": { "value": 1745571774 },
"exp": { "value": 1745575374 },
"amr": { "values": ["pwd"] }
}
}' | jq
var payload = JSON.stringify(
{
claims: {
external_id: { value: "john_doe-101" },
auth_time: { value: 1745571774 },
exp: { value: 1745575374 },
amr: { values: ["pwd"] },
},
},
null,
2,
);
var url = `${PAPEROS_BASE_URL}/api/v1/integrations/id-token`;
var basicAuth = btoa(`${oidc.client_id}:${oidc.client_secret}`);
var resp = await fetch(url, {
method: "POST",
credentials: "include",
headers: {
Authorization: `Basic ${basicAuth}`,
"Content-Type": "application/json",
},
body: payload,
});
var accessTokenResult = await resp.json();
console.log(accessTokenResult);
Example Response:
{
"id_token": "eyJ0eXAiOiJKV1QiLCJraWQiOiJTcG5TVEkyc1p3d0p3aV9oWVJ4VFFnOGlJZHdBMS0zUG81c1NsVUptUXdjIiwiYWxnIjoiRVMyNTYifQ.eyJqdGkiOiJkWXZfZHduTWtIajd3ZjNaM0FQbHZ3IiwiaXNzIjoiaHR0cHM6Ly9wYXBlcm9zLWRldi03LjExMDQuYy5ibm5hLm5ldCIsInN1YiI6InN1Yl9kMDI1NWE4cjNoa25uenNoIiwiYXV0aF90aW1lIjoxNzQ1NDQ3NzQ4LCJjbGllbnRfaWQiOiJvaWRjX3pkY3c5M2s3aDR3NHpiMm0iLCJlbWFpbCI6ImpvaG4uZG9lKzEwMUBleGFtcGxlLmNvbSIsImdpdmVuX25hbWUiOiJKb2huIiwiZmFtaWx5X25hbWUiOiJEb2UiLCJvcmdfaWQiOiJvcmdfZXc0MjFqcjRwbW5oczAyeiIsImlhdCI6MTc0NTQ1MDE0OSwiZXhwIjoxNzQ1NDUzNzQ5fQ.YuvVXJcwfsBJT00SzOHuphh1pt8KXJEMoPmZxKJJWCDrDPbrcI2vX6YNcKtgLOVcv7lKMK1-YKvJBNgAF_N-iA"
}
Create Access Token
The ID Token represents access to a specific organization through the subject (user).
POST /api/v1/integrations/access-token
curl -v --fail-with-body -X POST "${PAPEROS_BASE_URL}/api/v1/integrations/access-token" \
--user "${CLIENT_ID}:${CLIENT_SECRET}" \
-H "Content-Type: application/json" \
-d '{
"claims": {
"external_id": { "value": "john_doe-101" },
"org_id": { "value": "org_ew421jr4pmnhs02z" },
"auth_time": { "value": 1745571774 },
"exp": { "value": 1745575374 },
"amr": { "values": ["pwd"] }
}
}' | jq
var payload = JSON.stringify(
{
claims: {
external_id: { value: "john_doe-101" },
org_id: { value: "org_ew421jr4pmnhs02z" },
auth_time: { value: 1745571774 },
exp: { value: 1745575374 },
amr: { values: ["pwd"] },
},
},
null,
2,
);
var url = `${PAPEROS_BASE_URL}/api/v1/integrations/access-token`;
var basicAuth = btoa(`${oidc.client_id}:${oidc.client_secret}`);
var resp = await fetch(url, {
method: "POST",
credentials: "include",
headers: {
Authorization: `Basic ${basicAuth}`,
"Content-Type": "application/json",
},
body: payload,
});
var accessTokenResult = await resp.json();
console.log(accessTokenResult);
Example Response:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJraWQiOiJTcG5TVEkyc1p3d0p3aV9oWVJ4VFFnOGlJZHdBMS0zUG81c1NsVUptUXdjIiwiYWxnIjoiRVMyNTYifQ.eyJqdGkiOiJkWXZfZHduTWtIajd3ZjNaM0FQbHZ3IiwiaXNzIjoiaHR0cHM6Ly9wYXBlcm9zLWRldi03LjExMDQuYy5ibm5hLm5ldCIsInN1YiI6InN1Yl9kMDI1NWE4cjNoa25uenNoIiwiYXV0aF90aW1lIjoxNzQ1NDQ3NzQ4LCJjbGllbnRfaWQiOiJvaWRjX3pkY3c5M2s3aDR3NHpiMm0iLCJlbWFpbCI6ImpvaG4uZG9lKzEwMUBleGFtcGxlLmNvbSIsImdpdmVuX25hbWUiOiJKb2huIiwiZmFtaWx5X25hbWUiOiJEb2UiLCJvcmdfaWQiOiJvcmdfZXc0MjFqcjRwbW5oczAyeiIsImlhdCI6MTc0NTQ1MDE0OSwiZXhwIjoxNzQ1NDUzNzQ5fQ.YuvVXJcwfsBJT00SzOHuphh1pt8KXJEMoPmZxKJJWCDrDPbrcI2vX6YNcKtgLOVcv7lKMK1-YKvJBNgAF_N-iA"
}
Inspect Token
GET /api/user/debug
curl --fail-with-body "${PAPEROS_BASE_URL}/api/user/debug" \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}" |
jq
var url = `${paperBase}/api/user/debug`;
var resp = await fetch(url, {
headers: { Authorization: `Bearer ${paperToken}` },
});
var data = await resp.json();
Example Response:
{
"user": {
"account_id": null,
"partner_id": null,
"auth_time": "2023-09-19T22:25:54.000Z",
"iat": "2023-09-19T22:25:54.000Z",
"exp": null,
"api_token": true,
"email": "services+test1@savvi.legal"
},
"method": "GET",
"originalUrl": "/api/user/debug"
}
Use the user and account debug endpoints to inspect details of the token.
Organizations
List (v1-draft)
GET /api/v1/orgs?updated_since=0
my_orgs="$(
curl "${PAPEROS_BASE_URL}/api/v1/orgs?updated_since=0" \
-H "Authorization: Bearer ${OIDC_ID_TOKEN}"
)"
echo "${my_orgs}" |
jq
var url = `${paperBase}/api/v1/orgs?updated_since=0`;
var resp = await fetch(url, {
headers: { Authorization: `Bearer ${paperToken}` },
});
var orgs = await resp.json();
console.log(orgs);
Example Response:
{
"updated_at": 1677000469,
"orgs": [
{
"id": "org_01ewdxxpvgg2y19pbtbyddtvv8",
"name": "Test 1",
"brand_id": "brand_00000000000000000000000000",
"created_at": "2021-01-19T18:18:46.000Z",
"updated_at": "2023-02-21T17:27:49.000Z"
}
]
}
Retrieve all orgs associated with this user, including through direct ownership, delegation, or partnerships.
Query Parameters
| Parameter | Default | Description |
|---|---|---|
updated_since |
'' | required, pass 0 or the previous updated_since |
View Token Debug Info
GET /api/v1/org/debug
my_org_id="$(
echo "${my_orgs}" |
jq '.[0].id'
)"
echo "${my_org_id}"
curl "${PAPEROS_BASE_URL}/api/v1/org/debug?account_id=${my_org_id}" \
-H "Authorization: Bearer ${OIDC_ID_TOKEN}" |
jq
var orgId = orgs[0].id;
var url = `${paperBase}/api/v1/org/debug?account_id=${orgId}`;
var resp = await fetch(url, {
headers: { Authorization: `Bearer ${paperToken}` },
});
var orgInfo = await resp.json();
Example Response:
{
"user": {
"account_id": 97,
"partner_id": null,
"auth_time": "2023-09-19T22:25:54.000Z",
"iat": "2023-09-19T22:25:54.000Z",
"exp": null,
"api_token": true,
"email": "services+test1@savvi.legal"
},
"method": "GET",
"originalUrl": "/api/v1/org/debug?account_id=97"
}
Show account token details
Query Parameters
| Parameter | Default | Description |
|---|---|---|
account_id |
'' | Either required or disallowed, based on token type. |
Create (v1-draft)
POST /api/v1/orgs
curl "${PAPEROS_BASE_URL}/api/v1/orgs" \
-X 'POST' \
-H "Authorization: Bearer ${OIDC_ID_TOKEN}" \
-H 'Content-Type: application/json' \
--data-raw '{
"name": "My Test Company 11",
"fields": {
"business_type": "for_profit"
}
}' |
jq
var data = {
name: "My Test Company 11",
fields: {
business_type: "for_profit",
},
};
var payload = JSON.stringify(data, null, 2);
var url = `${paperBase}/api/v1/orgs`;
var resp = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${paperToken}`,
"Content-Type": "application/json",
},
body: payload,
});
var orgInfo = await resp.json();
Example Response:
{
"id": "org_01hbsvp9tk3qthd2jjz2vzv0g8",
"name": "My Test Company 11",
"brand_id": "brand_01h2stkn1fqe8dcfmyrq7thpab",
"created_at": "2023-10-03T04:10:43.000Z",
"updated_at": "2023-10-03T04:10:49.000Z"
}
Create a new organization.
Options for business_type are:
for_profitinvestment_vehicleprofessional_servicesother
Records
Records are the collections of data that can be both the result of form submissions, and made available for autofill selection to future forms.
Create One
POST /api/v1/orgs/:org_id/records
curl "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/records" \
-X 'POST' \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}" \
-H 'Content-Type: application/json' \
--data-raw '{
"type": "indv",
"name": "Jane Doe",
"fields": {
"title": "master painter",
"email": "jane@jane.doe",
"is_board_director": "1"
}
}' |
jq
var data = {
type: "indv",
name: "Jane Doe",
fields: {
title: "master painter",
email: "jane@jane.doe",
is_board_director: "1",
},
};
var payload = JSON.stringify(data, null, 2);
var url = `${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/records`;
var resp = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: payload,
});
var recordInfo = await resp.json();
Example Response:
{
"success": true,
"type": "string",
"rec_id": "rec_01hcey7qcfeeqmh1af6x3xafa2"
}
Create a new record belonging to this organization.
POST /api/v1/orgs/{my_org_id}/records
Record Types
Below is a small subset of our record types, reach out for a tailored list fitting your needs.
| Slug | Code |
|---|---|
activity |
acty |
annual_report |
ann_rpt |
contract |
k |
doc |
doc |
equity |
eq |
equity_class |
eq_cl |
financing |
fin |
individual |
indv |
investment |
invt |
ip |
ip |
legal_audit |
lgl_adt |
org |
org |
pii |
pii |
questionnaire |
qre |
state |
st |
task |
task |
tax_filing |
tax |
tos |
tos |
List All by Type
GET /api/v1/orgs/{org_id}/records?type={type_slug}
curl -G "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/records" \
--data-urlencode "type=org" \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}" |
jq
var params = { type: "org" };
var search = new URLSearchParams(params).toString();
var url = `${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/records?${search}`;
var resp = await fetch(url, {
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
},
});
var resInfos = await resp.json();
Example Response:
[
{
"id": 5617,
"name": "Test 1",
"resource_type_id": 2,
"account_id": 97,
"created_at": "2021-01-19T18:18:49.000Z",
"updated_at": "2021-01-19T18:18:49.000Z",
"finalized": 0,
"archived": 0,
"is_draft": 0,
"features": {
"name": "Test 1"
}
},
{
"id": 17413,
"name": "Bob the Builder",
"resource_type_id": 1,
"account_id": 97,
"created_at": "2023-09-22T19:50:13.000Z",
"updated_at": "2023-09-22T19:50:13.000Z",
"finalized": 0,
"archived": 0,
"is_draft": 0,
"features": {
"name": "Bob the Builder",
"signatory_name": "Bob the Builder",
"first_name": "Bob",
"last_name": "Builder",
"middle_name": "the",
"title": "master builder",
"email": "bob@bobbuild.bob",
"employee_documents_list": "All of the above",
"upload_or_generate": "Generate"
}
}
]
Records are scoped to a specific account.
TODO don't allow creating completely empty entities
| Query | Description |
|---|---|
type |
the record type slug, such as individual or org (* for any) |
rec_id |
a single record ids (begins with rec_) |
rec_ids |
a comma-separated list of record ids (begin with rec_) |
since |
an ISO timestamp of the last record received (second resolution) |
limit |
return only n records |
Get One by ID
GET /api/v1/orgs/:org_id/records/:rec_id
my_rec_id='17413'
curl "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/records/${my_rec_id}" \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}" |
jq
var myRecId = "17413";
var url = `${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/records/${myRecId}`;
var resp = await fetch(url, {
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
},
});
var recordInfo = await resp.json();
Example Response:
{
"id": 17413,
"name": "Bob the Builder",
"resource_type_id": 1,
"account_id": 97,
"created_at": "2023-09-22T19:50:13.000Z",
"updated_at": "2023-09-22T19:50:13.000Z",
"finalized": 0,
"archived": 0,
"is_draft": 0,
"features": {
"name": "Bob the Builder",
"signatory_name": "Bob the Builder",
"first_name": "Bob",
"last_name": "Builder",
"middle_name": "the",
"title": "master builder",
"email": "bob@bobbuild.bob",
"employee_documents_list": "All of the above",
"upload_or_generate": "Generate"
}
}
Show details for a resource by its ID.
Update One by ID
PATCH /api/v1/orgs/:org_id/records/:rec_id
my_rec_id='5617'
curl "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/records/${my_rec_id}" \
-X 'PATCH' \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}" \
-H 'Content-Type: application/json' \
--data-raw '{
"fields": {
"title": "Product Manager",
"email": "john2@john.doe",
}
}' |
jq
var myRecId = "5617";
var data = {
fields: {
title: "Product Manager",
email: "john2@john.doe",
},
};
var payload = JSON.stringify(data, null, 2);
var url = `${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/records/${myRecId}`;
var resp = await fetch(url, {
method: "PATCH",
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: payload,
});
var resInfo = await resp.json();
Example Response:
{
"success": true,
"type": "[]<string>",
"total": 2,
"changes": ["title", "email"],
"count": 2
}
Update the properties of an existing resource
Workflows
The Workflow Library shows each of the available form collections for a particular task.
List All Workflow Templates
Get list of all available Workflow Templates.
GET /api/v1/orgs/:org_id/workflow-templates
curl "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/workflow-templates" \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}" |
jq
var url = `${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/workflow-templates`;
var resp = await fetch(url, {
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
},
});
var library = await resp.json();
console.log(library);
Example Response: (simplified for readability)
{
"success": true,
"count": 71,
"total": 71,
"type": "[]<workflow_template>",
"workflow_templates": [
{
"id": "flow_extg0vcffk3h9085",
"label": "Entity Setup",
"description": "Form a new entity, or upload your documentation for an existing entity.",
"created_at": "2023-12-29T18:56:00.000Z",
"updated_at": "2025-05-08T22:07:38.000Z"
},
{
"id": "flow_extg0yj9vywzabwy",
"label": "Fundraising Diligence Portal",
"description": "Share pitch materials with prospective investors for initial screening. Set up custom Diligence Rooms to respond to requests & share relevant Data Room documentation.",
"created_at": "2023-12-29T18:56:00.000Z",
"updated_at": "2025-05-08T22:03:20.000Z"
},
{
"id": "flow_extg0g5yjfrtfk3e",
"label": "Onboard Employees",
"description": "Onboard employees and generate appropriate documents. Or upload the documents that you've already executed.",
"created_at": "2023-12-29T18:56:00.000Z",
"updated_at": "2025-05-08T22:07:44.000Z"
},
{
"id": "flow_extg0z7hs83avjbt",
"label": "Annual Report (State Renewal)",
"description": "Complete the annual report required by each state in which your company is registered.",
"created_at": "2023-12-29T18:56:00.000Z",
"updated_at": "2024-11-20T18:21:08.000Z"
},
{
"id": "flow_extg0h07e96b2etx",
"label": "Data Room Setup",
"description": "Set up a custom data room that you can manage and share with relevant parties.",
"created_at": "2023-12-29T18:56:00.000Z",
"updated_at": "2023-12-02T00:33:47.000Z"
},
{
"id": "flow_extg0v4ndq47rnwk",
"label": "Doc Send & Sign",
"description": "Send and Sign Custom Documents.",
"created_at": "2023-12-29T18:56:00.000Z",
"updated_at": "2024-10-31T00:05:52.000Z"
}
// ...rest of available templates
]
}
List All Started Workflows
Get List of started Workflows. Each Workflow was generated from a Workflow Template above.
GET /api/v1/orgs/:org_id/workflows
curl "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/workflows" \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}" |
jq
var url = `${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/workflows`;
var resp = await fetch(url, {
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
},
});
var workflows = await resp.json();
console.log(workflows);
Example Response:
{
"success": true,
"total": 1,
"count": 1,
"workflows": [
{
"id": "work_q0yjdgdr77rdnedc",
"label": "Entity Setup",
"template_id": "flow_extg0vcffk3h9085",
"status": "open",
"created_at": "2025-10-22T19:41:06.000Z",
"updated_at": "2025-10-22T19:50:07.000Z"
}
]
}
Start a Workflow
POST /api/v1/orgs/:org_id/workflows
curl "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/workflows" \
-X 'POST' \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
--data-raw '{
"flow_id": "${my_flow_id}"
}' |
jq
var data = {
flow_id: my_flow_id,
};
var payload = JSON.stringify(data, null, 2);
var url = `${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/workflows`;
var resp = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: payload,
});
var workflow = await resp.json();
console.log(workflow);
Example Response:
{
"status": "success",
"workflow_id": "wrk_01hcn8arzxb9heq3saaf97b7bx"
}
Opening a workflow will create the associated To-Do items in the PaperOS interface.
Start a Workflow with Prepopulated Data & Auto Submit
POST /api/v1/orgs/:org_id/workflows
curl "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/workflows" \
-X 'POST' \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
--data-raw '{
"flow_id": "${my_flow_id}",
"records": [{
"role": "company",
"id": "rec_kp9fh3354dqt255m"
}],
"auto_submit": 1
}' |
jq
var data = {
flow_id: my_flow_id,
records: [
{
role: "company",
id: "rec_kp9fh3354dqt255m",
},
],
auto_submit: 1,
};
var payload = JSON.stringify(data, null, 2);
var url = `${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/workflows`;
var resp = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: payload,
});
var workflow = await resp.json();
console.log(workflow);
Example Response:
{
"status": "success",
"workflow_id": "wrk_01hcn8arzxb9heq3saaf97b7bx"
}
Prepopulate a new workflow instance:
- See the Records section to create the resources that will be referenced here.
- List those resources when opening a workflow to make them available for autofill.
TODO: create slug for library item
Documents
A combination of documents waiting to be signed, completed, or uploaded. If document requires signatures, the signature links will be passed through recipients[].
Get Documents by Org
Get a list of documents for a specific account
GET /api/v1/orgs/{{org_id}}/documents
GET /api/v1/org/documents?account_id={{account_id}}
curl "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/documents" \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}"
curl "$PAPEROS_BASE_URL/api/v1/org/documents?account_id=${account_id}" \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}"
var url = `${paperBase}/api/v1/orgs/${my_org_id}/documents`;
var resp = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
},
});
var url = `${paperBase}/api/v1/org/documents?account_id=${account_id}`;
var resp = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
},
});
var resource = await resp.json();
console.log(resource);
Get Documents by Email
Get a list of documents for a specific account & email.
GET /api/v1/orgs/{{org_id}}/documents?email={{email}}
GET /api/v1/org/documents?account_id={{account_id}}&email={{email}}
curl "${PAPEROS_BASE_URL}/api/v1/orgs/${my_org_id}/documents?email=${email}" \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}"
curl "${PAPEROS_BASE_URL}/api/v1/org/documents?account_id=${account_id}&email=${email}" \
-H "Authorization: Bearer ${OIDC_ACCESS_TOKEN}"
var url = `${paperBase}/api/v1/orgs/${my_org_id}/documents?email=${email}`;
var resp = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
},
});
var url = `${paperBase}/api/v1/org/documents?account_id=${account_id}&email=${email}`;
var resp = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${OIDC_ACCESS_TOKEN}`,
},
});
var resource = await resp.json();
console.log(resource);
Example Response:
{
"success": true,
"total": 3,
"count": 3,
"type": "[]<document>",
"documents": [
{
"path": "/Archived Documentation/Historical Financials",
"filename": "2017 - 2020 Balance Sheet.xlsx",
"recipients": [],
"url": "https://paperos.com/api/public/documents/820701358552/eyJ0eXAiOiJKV1QiLCJraWQiOiJKa3BxUW1faW9IeHRsb1BOTS12VE1IenkzR0xWLW1GbEhDdkxPMVJ0RlhVIiwiYWxnIjoiRVMyNTYifQ.eyJmaWxlIjoiODIwNzAxMzU4NTUyIiwiaWF0IjoxNjk3MjI3NTA0LCJleHAiOjE2OTcyMjg0MDQsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMSJ9.iNPasWw1VfMDuNTTDHW16f5CgmXgKJUoyY9I6Ac2RPosGn61GSMDMgPXzi10uSk2Mg9SjtWclZxdIe5t6z-Lqw",
"pub_id": "doc_0000000000p6a290bsjjqmkfk1"
},
{
"path": "/Archived Documentation/Historical Financials",
"filename": "2017 - 2020 PnL.xlsx",
"recipients": [],
"url": "https://paperos.com/api/public/documents/820688814651/eyJ0eXAiOiJKV1QiLCJraWQiOiJKa3BxUW1faW9IeHRsb1BOTS12VE1IenkzR0xWLW1GbEhDdkxPMVJ0RlhVIiwiYWxnIjoiRVMyNTYifQ.eyJmaWxlIjoiODIwNjg4ODE0NjUxIiwiaWF0IjoxNjk3MjI3NTA0LCJleHAiOjE2OTcyMjg0MDQsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMSJ9.WMcM5HULwVyVmiDMMVtibStMou6C_O3Z2886VNZko7U7dBvOrqgPMcYaX0Ilt10q_iM_aj7LF29pNfTgxuXLxQ",
"pub_id": "doc_00000000000b4j5gfbg8hb0sp6"
},
{
"path": "/Archived Documentation/Historical Financials",
"filename": "2017 - 2020 Statement of Cash Flows.xlsx",
"recipients": [],
"url": "https://paperos.com/api/public/documents/820700190254/eyJ0eXAiOiJKV1QiLCJraWQiOiJKa3BxUW1faW9IeHRsb1BOTS12VE1IenkzR0xWLW1GbEhDdkxPMVJ0RlhVIiwiYWxnIjoiRVMyNTYifQ.eyJmaWxlIjoiODIwNzAwMTkwMjU0IiwiaWF0IjoxNjk3MjI3NTA0LCJleHAiOjE2OTcyMjg0MDQsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMSJ9.HXaecHH5bq1QAw5TRQCqnb1dn8dE7spcQpLU1DM6J8AJYraTRqeOcLjRziA9P83YbFgMCzHKY-cDDiGsGoUVlw",
"pub_id": "doc_0000000000y62e1skpa27jywhv"
}
]
}
Errors
The PaperOS API uses the following error codes:
4xx
| Error Code | Meaning |
|---|---|
| 400 | Bad Request – Some of the query params or body props were missing or invalid |
| 401 | Unauthorized – The API key, user, or account is invalid or expired |
| 402 | Payment Required – The account must be upgraded to use this feature |
| 403 | Forbidden – The given key, user, or account doesn't have access |
| 404 | Not Found – The specified data was not found |
| 405 | Method Not Allowed – A GET was used where a POST was required, etc |
| 406 | Not Acceptable – The data was not in the proper format (i.e. JSON, CSV) |
| 420 | Enhance Your Calm – You're doing that too fast |
| 422 | Unprocessable Content – The format is correct, but the data is not |
| 429 | Too Many Requests – alias of 420 |
5xx
| Error Code | Meaning |
|---|---|
| 500 | Internal Server Error – We had a problem with our server. Please let us know |
| 501 | Not Implemented – The feature you tried to use is still in development |
| 502 | Bad Gateway – The application may be restarting |