generated from coulomb/repo-seed
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>
This commit is contained in:
295
AGENTS.md
Normal file
295
AGENTS.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user