API integration

Chaindoc's REST API lets you automate document workflows, collect signatures, and sync events in real time. This page covers how to get started, the main concepts, and common integration patterns.

What you can do with the API

The API gives you programmatic access to everything Chaindoc does: upload documents, create signature requests, manage teams, and listen for events via webhooks. It's the same API that powers the web app.

Three ways to integrate

Pick the one that fits your stack. Most production apps use the Server SDK on the backend and the Embed SDK on the frontend.

  • REST API — direct HTTP requests, works with any language. Maximum flexibility.
  • Server SDK — type-safe Node.js wrapper with automatic retries and error handling (`@chaindoc_io/server-sdk`)
  • Embed SDK — drops a signing interface into your web app so users don't leave your site (`@chaindoc_io/embed-sdk`)

If you haven't installed the SDKs yet, the installation guide covers npm setup for React, Vue, Angular, and Next.js.

Quick example

Here's the same "create a document" call in three flavors. The SDK version is the most concise, but the raw REST call shows exactly what's happening over the wire.

curl -X POST https://api.chaindoc.io/api/v1/documents \
  -H "Authorization: Bearer sk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Contract",
    "description": "Service agreement",
    "status": "published",
    "hashtags": ["#contract"],
    "meta": []
  }'

Authentication

Every request needs an API key in the Authorization header:

terminal
Authorization: Bearer sk_live_xxxxxxxxxxxxx

There are two types of keys, and mixing them up is the most common integration mistake:

  • Public keys (`pk_`) — read-only, safe for frontend code
  • Secret keys (`sk_`) — full read/write, backend only. Never expose these in client-side code.
  • Both come in test (`_test_`) and live (`_live_`) variants. Test keys hit the sandbox.

Rate limits

The API returns rate limit info in response headers. If you hit the limit, you'll get a 429 response. Back off and retry.

terminal
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 8
X-RateLimit-Reset: 1640000000
  • General endpoints: 3 requests per 10 seconds
  • Media upload: 3 requests per 10 seconds
  • Read operations: 10 requests per 60 seconds
  • Signature creation: 20 requests per 3 seconds

The Server SDK handles retries automatically with exponential backoff. If you're using raw HTTP, implement retry logic yourself.

Error handling

Errors come back as JSON with a status code, message, and (for validation errors) a list of specific field issues:

terminal
{
  "statusCode": 400,
  "message": "Validation failed",
  "error": "Bad Request",
  "details": [
    {
      "field": "name",
      "message": "name must be a string"
    }
  ]
}

Common status codes: 400 (bad request), 401 (invalid key), 403 (plan doesn't include API), 404 (not found), 429 (rate limited), 500 (server error). For 5xx errors, retry with backoff.

Common integration patterns

Full signing flow (embedded)

The most common pattern: upload a document, send it for signatures, and show the signing UI inside your app.

1Upload the filePOST /media/upload — send the PDF, Office doc, or image.

2Create the documentPOST /documents — set status='published' to trigger blockchain verification.

3Create a signature requestPOST /signatures/requests — add recipients, set the signing order and deadline.

4Create an embedded sessionPOST /embedded/sessions — this gives you a sessionId for each signer.

5Open the signing UIPass the sessionId to the Embed SDK's openSignatureFlow() method.

6Listen for completionSet up a webhook for the signature.request.completed event, or poll GET /signatures/requests/:id/status.

The quick start guide has the full code for this flow.

Email-based signing (no embed)

If signers don't use your app, set `embeddedFlow: false`. Chaindoc sends them an email with a signing link. You still get webhook notifications when they sign.

terminal
const sigRequest = await chaindoc.signatures.createRequest({
  versionId: documentVersionId,
  recipients: [{ email: 'signer@example.com' }],
  deadline: new Date('2024-12-31'),
  embeddedFlow: false, // Signers get email links
  message: 'Please review and sign this document',
});

Bulk document processing

For batch imports, loop through your files and upload each one. Watch the rate limits though. If you're processing hundreds of documents, add a small delay between requests or use the SDK's built-in retry logic.

terminal
const documents = ['doc1.pdf', 'doc2.pdf', 'doc3.pdf'];

for (const file of documents) {
  const buffer = await readFile(file);
  const blob = new Blob([buffer], { type: 'application/pdf' });
  
  const { media } = await chaindoc.media.upload([blob]);
  
  await chaindoc.documents.create({
    name: file,
    description: 'Batch processed document',
    media: media[0],
    status: 'published',
    hashtags: ['#batch'],
    meta: [{ key: 'batch', value: 'import-2024' }],
  });
}

KYC integration

For high-security workflows, require identity verification before signing. Chaindoc integrates with Sumsub for KYC. Set `isKycRequired: true` on the signature request, and signers will go through identity verification before they can sign.

terminal
const sigRequest = await chaindoc.signatures.createRequest({
  versionId: documentVersionId,
  recipients: [{ 
    email: 'signer@example.com',
    shareToken: 'sumsub_token_here' // Optional: pre-verified
  }],
  deadline: new Date('2024-12-31'),
  isKycRequired: true,
});

Access control via API

You can set document permissions programmatically. Restrict access to specific emails or team roles:

terminal
await chaindoc.documents.updateRights(documentId, {
  accessType: 'restricted',
  accessEmails: [
    { email: 'viewer@company.com', level: 'read' },
    { email: 'editor@company.com', level: 'write' },
  ],
  accessRoles: [
    { roleId: 1, level: 'read' },
  ],
});

Best practices

  • Keep API keys in environment variables. Never commit them to version control.
  • Use test keys (`sk_test_`) for development. Live keys (`sk_live_`) hit production.
  • Implement exponential backoff for 429 responses. The SDK does this automatically.
  • Verify webhook signatures with HMAC to prevent replay attacks.
  • Upload files before creating documents. The API needs the media ID from the upload step.
  • Use pagination for endpoints that return lists. Don't fetch everything at once.
  • Check the security guide before going live.

Example use cases

A few real-world patterns teams build with the API:

  • E-commerce — auto-generate purchase agreements for high-value orders and send for signing at checkout
  • HR onboarding — create employment contracts from templates and send to new hires on their start date
  • CRM integration — trigger signature requests from Salesforce or HubSpot deal records
  • Document archival — add blockchain verification to documents in your existing storage system

What to do next

  • API documentation — full endpoint reference with request/response examples
  • SDKs — Server SDK and Embed SDK with framework-specific guides
  • Webhooks — set up real-time event notifications
  • Quick start — get your first integration running in 10 minutes
  • Installation — npm setup for all supported frameworks