@t4dt/procuros-api-client
v2.0.0
Published
TypeScript client for the Procuros REST API v2
Maintainers
Readme
@t4dt/procuros-api-client
Kleiner TypeScript-HTTP-Client für die Procuros REST API v2. Läuft über fetch (Node 18+), ohne n8n – geeignet für eigene Services, Worker und Tests.
Geschäftsdaten (Bestellungen, Rechnungen, content in Listen) sind als TypeScript-Typen modelliert (Order, Invoice, SentTransaction, …). Umschlag, Queries und Steuer-Bodies bleiben ebenfalls strikt typisiert. Die Laufzeit-Antwort der API wird weiterhin geparst und an eure Typen übergeben – für harte Laufzeit-Garantien könnt ihr zusätzlich validieren (z. B. Zod).
Für Coding-Agenten (Cursor, Codex, …): Siehe AGENTS.md (Englisch, kompakte Referenz inkl. Endpoint-Tabelle und Pagination).
Installation
npm install @t4dt/procuros-api-clientpnpm add @t4dt/procuros-api-clientGrundkonfiguration
| Option | Pflicht | Beschreibung |
|--------|---------|----------------|
| baseUrl | ja | Basis-URL ohne abschließenden Slash, z. B. https://api.procuros.io |
| apiToken | ja | Bearer-Token für API v2 |
| fetchImpl | nein | Eigene fetch-kompatible Funktion (Tests, ältere Runtimes ohne globales fetch) |
Ohne globales fetch und ohne fetchImpl wirft der Konstruktor einen synchronen Error (Hinweis auf fehlendes fetch).
import { ProcurosApiClient } from '@t4dt/procuros-api-client';
const client = new ProcurosApiClient({
baseUrl: 'https://api.procuros.io',
apiToken: process.env.PROCUROS_TOKEN!,
});Fehlerbehandlung
Bei HTTP-Fehlerstatus (nicht 2xx) oder Netzwerk-/Transportfehlern wird ProcurosApiError geworfen:
message– lesbare Zusammenfassung (u. a. verschachteltes API-error,messageim JSON, Fallback mit Statuscode).statusCode– HTTP-Status bei fehlgeschlagener Antwort;undefined, wenn kein normales HTTP-Ergebnis vorlag (z. B. DNS, Timeout).responseBody– geparstes JSON (object/array/…), bei Parsefehler String, bei leerem Body oftundefined.
import { ProcurosApiClient, ProcurosApiError } from '@t4dt/procuros-api-client';
try {
await client.ping();
} catch (e) {
if (e instanceof ProcurosApiError) {
console.error(e.statusCode, e.responseBody);
}
throw e;
}Für die Interpretation typischer Fehlerlasten gibt es Hilfstypen (ValidationErrorBody, … in types.ts). Die API kann darüber hinaus Felder liefern; zur Laufzeit bleibt responseBody bewusst unknown-kompatibel über ProcurosApiError.
Methodenübersicht (REST v2)
| Methode | HTTP | Pfad (relativ zu baseUrl) |
|---------|------|------------------------------|
| ping() | GET | /v2/ping |
| listIncomingTransactions(query) | GET | /v2/transactions |
| sendTransaction(body) | POST | /v2/transactions |
| markTransactionProcessed(id, body) | PUT | /v2/transactions/{id} |
| bulkMarkTransactionsProcessed(body) | POST | /v2/transactions/bulk/mark-processed |
| listAllTransactions(query) | GET | /v2/all-transactions |
| showTransaction(id) | GET | /v2/all-transactions/{id} |
| reportOutgoingError(body) | POST | /v2/errors |
Transaktions-IDs in Pfaden werden intern URL-kodiert.
Pagination (Listen)
listIncomingTransactions und listAllTransactions liefern u. a. hasMore, nextCursor, perPage, count.
- Erste Seite: Aufruf ohne
cursor(optionalper_pageund Filter setzen). - Solange
hasMoreundnextCursor: erneut aufrufen mitcursor: nextCursor(Filter undper_pagebei Bedarf unverändert lassen).
Query-Parameter entsprechen der API, u. a. filter[type], filter[flow], filter[status], filter[created_between], per_page, cursor – siehe Typen ListTransactionsQuery und ListAllTransactionsQuery sowie die Procuros-Dokumentation zum exakten Format von filter[created_between].
Beispiele
Die folgenden Snippets setzen einen ProcurosApiClient voraus (siehe Grundkonfiguration).
Ping und eingehende Transaktionen
Jede Zeile in items ist ein ReceivedTransaction: das Feld type bestimmt die Form von content (z. B. bei 'ORDER' ein Order).
await client.ping();
const page1 = await client.listIncomingTransactions({ per_page: 50 });
const page2 = await client.listIncomingTransactions({
per_page: 50,
cursor: page1.nextCursor ?? undefined,
});
for (const tx of page1.items) {
switch (tx.type) {
case 'ORDER':
console.log(tx.content.header.orderIdentifier);
break;
case 'INVOICE':
console.log(tx.content.header.invoiceIdentifier);
break;
default:
break;
}
}Ausgehend senden (sendTransaction)
Der Body ist ein SentTransaction: immer type (z. B. 'ORDER') plus passend typisiertes content (Order, Invoice, …). Die Typen folgen der OpenAPI-/Procuros-Spezifikation; weicht die Live-API ab, hat die Dokumentation Vorrang – ggf. Paket aktualisieren oder bei Bedarf mit Assertion arbeiten.
Rückgabe: Promise<SendTransactionResult> mit SendTransactionResponse oder SendTransactionAcceptedResponse (z. B. asynchrone Annahme mit message). Enge mit typeof (result as { message?: string }).message === 'string' (siehe AGENTS.md) oder eigener Hilfsfunktion.
import type { SendTransactionResult, SentTransaction } from '@t4dt/procuros-api-client';
// client wie unter „Grundkonfiguration“
const order: SentTransaction = {
type: 'ORDER',
content: {
header: {
buyer: { identifiers: [{ identifier: '1100001016310', domain: 'GS1' }] },
supplier: { identifiers: [{ identifier: '1100001016312', domain: 'GS1' }] },
orderIdentifier: 'PO9383-R46',
orderDate: '2021-09-30T00:00:00.000000Z',
currency: 'EUR',
},
items: [
{
lineNumber: 1,
identifiers: [{ identifier: '4300348765432', domain: 'GS1' }],
isDepositItem: false,
quantity: 20,
unitOfMeasure: 'EA',
},
],
},
};
const result: SendTransactionResult = await client.sendTransaction(order);Eingehend als verarbeitet markieren
await client.markTransactionProcessed('…procuros-transaction-id…', {
success: true,
});Bulk mark processed
await client.bulkMarkTransactionsProcessed({
items: [
{ procurosTransactionId: 'id-1', success: true },
{ procurosTransactionId: 'id-2', success: false, errorReason: '…', errorType: 'DATA' },
],
});Alle Transaktionen filtern und Einzelabruf
listAllTransactions / showTransaction liefern Transaction: zusätzlich zu type und content enthält jeder Eintrag status, flow und createdAt.
const all = await client.listAllTransactions({
'filter[flow]': 'LIVE',
'filter[status]': 'PENDING',
per_page: 25,
});
const first = all.items[0];
if (first?.type === 'SHIPPING_NOTICE') {
console.log(first.content.header.shippingNoticeIdentifier);
}
const one = await client.showTransaction(all.items[0]!.procurosTransactionId);
if (one.data.type === 'ORDER') {
console.log(one.data.content.items.length);
}Ausgehenden Fehler melden
await client.reportOutgoingError({
errorReason: 'Mapping failed',
errorType: 'DATA',
errorContext: { field: 'lineItems[0].quantity' },
});Tests mit injiziertem fetch
const client = new ProcurosApiClient({
baseUrl: 'https://api.example.test',
apiToken: 'test-token',
fetchImpl: async (url, init) => ({
ok: true,
status: 200,
text: async () => '',
}),
});
await client.ping();Exportierte Typen und Konstanten
Aus dem Paket-Entry kommen u. a.:
- Umschlag & Listen:
ListReceivedTransactionsResponse,ListAllTransactionsQuery,SendTransactionResult, … - Transaktionen:
SentTransaction,ReceivedTransaction,Transaction,TransactionContent - Dokumente & Bausteine:
Order,OrderResponse,Invoice,CreditNote,ShippingNotice,Party,Tax, … (Modulsrc/domain, alles wird übertypes/indexre-exportiert) - API-Enums:
TransactionType,TransactionFlow,TransactionStatus,ErrorTypesowie KonstantenTRANSACTION_TYPES,TRANSACTION_FLOWS,TRANSACTION_STATUSES,ERROR_TYPES - Weitere Domänen-Enums: z. B.
UNIT_OF_MEASURES,MODE_OF_TRANSPORTS,ORDER_RESPONSE_HEADER_TYPES(siehesrc/domain/enums.ts)
Für generische JSON-Hilfen (z. B. eigene Erweiterungen): JsonObject, JsonValue aus demselben Entry.
Lizenz
MIT
