Kynthar API Documentation

AI-powered document processing. Upload invoices/POs, get structured data back.

Base URL: https://api.kynthar.com

Authentication

All requests need your API key in the header:

X-API-Key: your_api_key_here

Get your API key: Login → Settings → Copy API Key


Upload a Document

1. Upload file

POST /ingest/initiate
Content-Type: multipart/form-data

file: <your-pdf-file>

Response:

{
  "key": "uploads/abc123/invoice.pdf",
  "original_filename": "invoice.pdf"
}

2. Start processing

POST /ingest/finalize
Content-Type: application/json

{
  "key": "uploads/abc123/invoice.pdf",
  "original_filename": "invoice.pdf"
}

Response:

{
  "doc_id": "01HX...",
  "existed": false
}

Get Document

GET /api/documents/{doc_id}

Response:

{
  "doc_id": "01HX...",
  "file_name": "invoice.pdf",
  "doc_type": "invoice",
  "processing_status": "COMPLETED",
  "extracted_data": {
    "invoice_number": "INV-2025-001",
    "total_amount": 1250.00,
    "currency_code": "USD",
    "vendor_name": "Acme Corp",
    "invoice_date": "2025-01-15"
  }
}

Status values:


List Documents

GET /api/documents?limit=20&status=COMPLETED

Response:

{
  "documents": [...],
  "total": 156,
  "limit": 20,
  "offset": 0
}

Webhooks

Get notified instantly when documents finish processing.

Setup

  1. Go to Settings
  2. Enable webhooks
  3. Enter your URL: https://your-server.com/webhooks/kynthar
  4. Save

What You Receive

POST https://your-server.com/webhooks/kynthar
X-Webhook-Event: invoice.parsed
X-Webhook-Signature: sha256=abc...

{
  "event": "invoice.parsed",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "doc_id": "01HX...",
    "doc_type": "invoice",
    "processing_status": "COMPLETED"
  }
}

Your Response

Just return 200 OK within 5 seconds. Do heavy work async.

app.post('/webhooks/kynthar', (req, res) => {
  res.status(200).send('OK');  // Respond immediately

  // Process async
  handleWebhook(req.body).catch(console.error);
});

Verify Signature (Recommended)

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Email Upload

Email documents to your ingest address: {your-company-id}@ingest.kynthar.com

Get your address from Settings.


Rate Limits

When you hit a limit, you'll get 429 Too Many Requests.


Errors

All errors return JSON:

{
  "error": "Invalid API key"
}

Common errors:


Complete Example (Node.js)

const fetch = require('node-fetch');
const FormData = require('form-data');
const fs = require('fs');

const API_KEY = 'your_api_key';
const API = 'https://api.kynthar.com';

async function processDocument(filePath) {
  // 1. Upload
  const form = new FormData();
  form.append('file', fs.createReadStream(filePath));

  const init = await fetch(
    `${API}/ingest/initiate`,
    {
      method: 'POST',
      headers: { 'X-API-Key': API_KEY },
      body: form
    }
  ).then(r => r.json());

  // 2. Finalize
  const result = await fetch(
    `${API}/ingest/finalize`,
    {
      method: 'POST',
      headers: {
        'X-API-Key': API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        key: init.key,
        original_filename: init.original_filename
      })
    }
  ).then(r => r.json());

  console.log('Document ID:', result.doc_id);

  // 3. Poll for completion (or use webhooks!)
  while (true) {
    const doc = await fetch(
      `${API}/api/documents/${result.doc_id}`,
      { headers: { 'X-API-Key': API_KEY } }
    ).then(r => r.json());

    if (doc.processing_status === 'COMPLETED') {
      console.log('Done!', doc.extracted_data);
      return doc;
    }

    await new Promise(r => setTimeout(r, 5000));
  }
}

processDocument('./invoice.pdf');

Complete Example (Python)

import requests
import time

API_KEY = 'your_api_key'
API = 'https://api.kynthar.com'
headers = {'X-API-Key': API_KEY}

def process_document(file_path):
    # 1. Upload
    with open(file_path, 'rb') as f:
        init = requests.post(
            f'{API}/ingest/initiate',
            headers=headers,
            files={'file': f}
        ).json()

    # 2. Finalize
    result = requests.post(
        f'{API}/ingest/finalize',
        headers={
            **headers,
            'Content-Type': 'application/json'
        },
        json={
            'key': init['key'],
            'original_filename':
                init['original_filename']
        }
    ).json()

    doc_id = result['doc_id']
    print(f'Document ID: {doc_id}')

    # 3. Poll for completion
    while True:
        doc = requests.get(
            f'{API}/api/documents/{doc_id}',
            headers=headers
        ).json()

        if doc['processing_status'] == 'COMPLETED':
            print('Done!', doc['extracted_data'])
            return doc

        time.sleep(5)

process_document('invoice.pdf')

Complete Example (cURL)

# 1. Upload
INIT=$(curl -X POST \
  https://api.kynthar.com/ingest/initiate \
  -H "X-API-Key: your_api_key" \
  -F "file=@invoice.pdf")

KEY=$(echo $INIT | jq -r '.key')
FILENAME=$(echo $INIT | jq -r '.original_filename')

# 2. Finalize
RESULT=$(curl -X POST \
  https://api.kynthar.com/ingest/finalize \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d "{\"key\":\"$KEY\",\"original_filename\":\"$FILENAME\"}")

DOC_ID=$(echo $RESULT | jq -r '.doc_id')
echo "Document ID: $DOC_ID"

# 3. Get result
curl https://api.kynthar.com/api/documents/$DOC_ID \
  -H "X-API-Key: your_api_key"

Questions?

Email: support@kynthar.com