# 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](https://app.binect.de) 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: ```json // package.json { "dependencies": { "@binect/js": "file:../path/to/binect-js" } } ``` Or link it: ```bash cd /path/to/binect-js && npm link cd /your/project && npm link @binect/js ``` ## Quick Start ```typescript 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`) ```typescript // 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`) ```typescript // 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`) ```typescript // 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`) ```typescript // 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 ```typescript 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 ```typescript 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 ```typescript 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 ```typescript 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 ```typescript 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 ```typescript // 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 ```typescript // Recommended pattern for credentials const client = new BinectClient({ username: process.env.BINECT_USERNAME!, password: process.env.BINECT_PASSWORD! }); ``` ## Testing ```bash # 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).