Files
binect-js/AGENTS.md
tegwick 09422f4653 Add AGENTS.md for coding agent integration
Comprehensive guide for AI coding assistants to efficiently use the
Binect-JS library. Includes quick start, API methods, status codes,
helper functions, polling patterns, error handling, and complete
examples.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 23:16:38 +01:00

7.3 KiB

AGENTS.md - Binect-JS Library Usage Guide

This file helps coding agents (Claude, Cursor, Copilot, etc.) efficiently integrate and use the Binect-JS library for sending physical mail via PDF.

What This Library Does

Binect-JS is a TypeScript/JavaScript SDK for the Binect API that enables sending PDF documents as physical letters via Deutsche Post. Upload a PDF, the service extracts the recipient address, prints it, and mails it.

Installation

The library is not yet published to npm. Reference it locally:

// package.json
{
  "dependencies": {
    "@binect/js": "file:../path/to/binect-js"
  }
}

Or link it:

cd /path/to/binect-js && npm link
cd /your/project && npm link @binect/js

Quick Start

import { BinectClient, DocumentStatus, isShippable } from '@binect/js';
import { readFileSync } from 'fs';

const client = new BinectClient({
  username: 'your@email.com',
  password: 'your-password'
});

// Upload a PDF
const pdfContent = readFileSync('letter.pdf').toString('base64');
const doc = await client.documents.upload({
  content: pdfContent,
  filename: 'letter.pdf',
  color: false,
  simplex: false,  // false = duplex (double-sided)
  envelope: 'DINLANG',
  franking: 'STANDARD_FRANKING'
});

console.log(`Document ${doc.id} status: ${doc.status.code}`);

Core API Methods

Documents (client.documents)

// Upload PDF (base64 encoded)
const doc = await client.documents.upload({
  content: base64String,
  filename: 'letter.pdf',
  color: false,
  simplex: false,
  envelope: 'DINLANG',      // or 'C4'
  franking: 'STANDARD_FRANKING'
});

// Get document by ID
const doc = await client.documents.get(documentId);

// List shippable documents
const list = await client.documents.list({ limit: 10, offset: 0 });

// Delete document
await client.documents.delete(documentId);

// Get PDF preview
const response = await client.documents.getPdf(documentId);
const pdfBlob = await response.blob();

Sendings (client.sendings)

// Send a document (triggers physical mailing)
const sending = await client.sendings.send(documentId);

// Cancel a sending (only works if status is PRODUCTION_QUEUE or PRINTING)
const result = await client.sendings.cancel(documentId);

// Send multiple documents at once
const sendings = await client.sendings.announce([docId1, docId2]);

// Cancel multiple
const results = await client.sendings.cancelMultiple([docId1, docId2]);

Accounts (client.accounts)

// Get account balance
const account = await client.accounts.get();
console.log(`Balance: ${account.credit} ${account.unit}`); // e.g., "401 EUROCENT"

// Get personal data
const personal = await client.accounts.getPersonalData();

Attachments (client.attachments)

// Upload reusable attachment
const attachment = await client.attachments.upload({
  content: base64PdfContent,
  name: 'terms-and-conditions.pdf'
});

// Add attachment to document
await client.documents.addAttachment(documentId, attachment.attachmentId);

Document Status Codes

import { DocumentStatus } from '@binect/js';

DocumentStatus.IN_PREPARATION  // 1 - Being validated
DocumentStatus.SHIPPABLE       // 2 - Ready to send
DocumentStatus.PRODUCTION_QUEUE // 3 - Queued for printing
DocumentStatus.PRINTING        // 4 - Currently printing
DocumentStatus.SENT            // 5 - Mailed
DocumentStatus.CANCELED        // 6 - Canceled
DocumentStatus.ERRONEOUS       // 7 - Has errors

Helper Functions

import {
  isShippable,
  isErroneous,
  isCancelable,
  isTerminal,
  hasErrors,
  getErrors,
  pollUntil,
  waitForShippable,
  bufferToBase64,
  fileToBase64
} from '@binect/js';

// Status checks
if (isShippable(doc)) { /* ready to send */ }
if (isErroneous(doc)) { /* check errors */ }
if (isCancelable(doc)) { /* can still cancel */ }
if (isTerminal(doc)) { /* final state: sent/canceled/error */ }

// Validation
if (hasErrors(doc)) {
  const errors = getErrors(doc);
  errors.forEach(e => console.log(e.message));
}

// Base64 encoding
const base64 = bufferToBase64(fs.readFileSync('letter.pdf')); // Node.js
const base64 = await fileToBase64(fileInput.files[0]);        // Browser

Polling for Status Changes

import { pollUntil, isShippable, isErroneous } from '@binect/js';

// Poll until document is ready or has errors
const doc = await pollUntil(
  () => client.documents.get(documentId),
  (doc) => isShippable(doc) || isErroneous(doc),
  { intervalMs: 2000, maxAttempts: 30 }
);

// Or use the convenience helper
import { waitForShippable } from '@binect/js';
const doc = await waitForShippable(
  () => client.documents.get(documentId)
);

Error Handling

import { BinectApiError, BinectAuthError } from '@binect/js';

try {
  await client.documents.upload({ ... });
} catch (error) {
  if (error instanceof BinectAuthError) {
    // 401 - Invalid credentials
    console.error('Authentication failed');
  } else if (error instanceof BinectApiError) {
    // Other API errors (400, 403, 404, 500, etc.)
    console.error(`API Error: ${error.message}`);
    console.error(`Status: ${error.status}`);
    console.error(`Endpoint: ${error.endpoint}`);
    // Full details
    console.error(error.toDetailedString());
  }
}

Complete Send-and-Cancel Example

import { BinectClient, DocumentStatus, pollUntil } from '@binect/js';
import { readFileSync } from 'fs';

const client = new BinectClient({
  username: process.env.BINECT_USERNAME!,
  password: process.env.BINECT_PASSWORD!
});

// 1. Upload
const pdfContent = readFileSync('letter.pdf').toString('base64');
const doc = await client.documents.upload({
  content: pdfContent,
  filename: 'letter.pdf',
  envelope: 'DINLANG',
  franking: 'STANDARD_FRANKING'
});
const docId = String(doc.id);

// 2. Send
await client.sendings.send(docId);

// 3. Wait for production queue
const sentDoc = await pollUntil(
  () => client.documents.get(docId),
  (d) => d.status.code !== DocumentStatus.IN_PREPARATION,
  { intervalMs: 1000, maxAttempts: 10 }
);

// 4. Cancel if still possible
if (sentDoc.status.code === DocumentStatus.PRODUCTION_QUEUE ||
    sentDoc.status.code === DocumentStatus.PRINTING) {
  await client.sendings.cancel(docId);
}

// 5. Cleanup
await client.documents.delete(docId);

Type Imports

// All types are exported
import type {
  Document,
  DocumentUploadOptions,
  Sending,
  AccountInfo,
  ValidationMessage,
  PriceInfo,
  ListResponse,
  BinectClientConfig
} from '@binect/js';

Key Constraints

  1. PDF must have recipient address in DIN 5008 format (address window position)
  2. Max file size: 12 MB
  3. Authentication: HTTP Basic Auth (credentials not stored/cached)
  4. No retries: Network failures throw immediately (no automatic retry)
  5. Cancel window: Can only cancel while status is PRODUCTION_QUEUE (3) or PRINTING (4)

Environment Variables Pattern

// Recommended pattern for credentials
const client = new BinectClient({
  username: process.env.BINECT_USERNAME!,
  password: process.env.BINECT_PASSWORD!
});

Testing

# Run unit tests
npm test

# Run e2e tests (requires credentials)
BINECT_USERNAME="user@example.com" BINECT_PASSWORD="password" npm run test:e2e

Note: Use double quotes for passwords containing ! (bash history expansion).