This document describes how to use the OpenSign open-source deployment in this repository for API-based integrations.
Official hosted docs: OpenSign API v1.1
In this OSS codebase:
- The server is Parse-based and is mounted on
/app(or yourPARSE_MOUNT). - The frontend defaults to
${origin}/api/appwhen reverse proxied. - The built-in
openapi.jsonis legacy/incomplete and does not match runtime behavior. - v1.1-style routes like
/selfsignare not fully implemented as first-class Express endpoints in this repo. POST /createdocumentis implemented on this OSS server (under your Parse mount) for hosted-style compact payloads—see §4.
There are now two practical auth modes:
- Session token (existing OpenSign app behavior)
- Header:
X-Parse-Session-Token
- Header:
- API token (new in this implementation)
- Generate in UI:
Settings -> API Token - Header for token-protected routes:
X-Api-Token
- Generate in UI:
These routes are available on OpenSignServer custom routes:
GET /webhook(token-protected)POST /webhook(token-protected)POST /createdocument(token-protected; hosted-style create — see §4)
Authentication headers:
X-Api-Token: <your_token>
Example: get current webhook
curl -X GET "https://your-host/api/app/webhook" \
-H "X-Api-Token: os_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Example: save webhook configuration
curl -X POST "https://your-host/api/app/webhook" \
-H "Content-Type: application/json" \
-H "X-Api-Token: os_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-d '{
"url": "https://internal-app.example.com/opensign/webhook",
"secret": "replace-with-shared-secret",
"events": ["document.viewed", "document.completed", "document.declined"]
}'This endpoint accepts the same compact JSON body described in hosted API docs (file, name, note, signers) and maps it to contracts_Document, uploads the PDF, and sends signer invitation emails (same mail path as quick/bulk send).
URL
{ParseBase}/createdocument
Example:https://your-host/api/app/createdocument
Headers
Content-Type: application/jsonX-Api-Token: <token_from_Settings_API_Token>- Optional (if your reverse proxy or client requires them):
X-Parse-Application-Id,X-Parse-REST-API-Key, etc. — the route authenticates viaX-Api-Tokenonly.
Body
file(string, required): Base64-encoded PDF.name(string, optional): Saved document / file name (.pdffriendly).note(string, optional): Shown in invitation mail context.signers(array, required): Each item:email(string, required)name(string, optional)widgets(array, required): Each widget:type(string, optional, defaultsignature)page(number, optional, default1) — 1-based page indexx,y,w,h(numbers, required): Positions in PDF coordinate space, matching hosted widget semantics (maps internally to OpenSignxPosition,yPosition,Width,Height).options(object, optional): e.g.{ "name": "sig_field", "hint": "Sign here" }
Minimal example:
curl -X POST "https://your-host/api/app/createdocument" \
-H "Content-Type: application/json" \
-H "X-Api-Token: os_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-d '{
"file": "<base64_pdf_here>",
"name": "Agreement.pdf",
"note": "Please sign",
"signers": [
{
"email": "signer1@example.com",
"name": "Signer One",
"widgets": [
{ "type": "signature", "page": 3, "x": 72, "y": 520, "w": 200, "h": 40,
"options": { "name": "sig_1", "hint": "Signer One" } }
]
}
]
}'Success response (200)
{
"objectId": "<contracts_Document objectId>",
"createdAt": "<ISO8601>",
"result": { "objectId": "<same objectId>" }
}Error responses
400— Invalid JSON body, missingfile/signers, invalid widgets (e.g. non-numeric geometry).401— Missing/invalidX-Api-Token.400— Authenticated user has nocontracts_Usersrow yet (complete OpenSign signup / extension profile first).500— PDF processing, storage (MASTER_KEY/ files adapter), or unexpected server error.
Prerequisites
- API owner must exist as
_Userwith a linkedcontracts_Usersrecord (normal OpenSign account). - File uploads use
MASTER_KEY(same as other server utilities).
For OSS deployment you can still call Parse REST + Cloud Functions under /app.
Common base:
- Parse base URL:
https://your-host/api/app
Core headers:
X-Parse-Application-Id: opensign(or your configuredAPP_ID)- Session-based flow:
X-Parse-Session-Token: <session_token>
Useful Cloud Functions:
POST /functions/getDocumentPOST /functions/getReportPOST /functions/batchdocumentsPOST /functions/signPdfPOST /functions/triggerevent
Create document record directly (advanced):
POST /classes/contracts_Document
Note: raw contracts_Document payloads are rich (signers, placeholders/widgets, ACL, tenant-linked fields). Prefer POST /createdocument (§4a) when you only have a compact hosted-style payload.
When document events are triggered via triggerevent, OpenSign sends callbacks to your configured webhook URL for subscribed events.
Supported events in this implementation:
document.vieweddocument.completeddocument.declined
Delivery signature:
- Header:
X-OpenSign-Signature - Value:
hex(hmac_sha256(raw_json_body, webhook_secret))
Verification pseudo-code:
const expected = crypto.createHmac("sha256", webhookSecret).update(rawBody).digest("hex");
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));- Hosted docs are useful for product-level API concepts.
- This OSS runtime uses Parse endpoints and Cloud Functions as the source of truth.
POST /createdocument(§4a) provides hosted-compatible document creation for OSS; other v1.1 paths (/selfsign, etc.) may still need adapters if you rely on them explicitly.