# Thing Event System (TES) — Full Reference > A drop-in token-compression layer for any LLM. TES proxies requests to Anthropic, OpenAI, MiniMax, or any OpenAI-compatible endpoint — retrieving the context the model would have re-derived this turn and injecting it as a preamble. 95% fewer input tokens on code and agentic workloads; see /benchmarks for the reproducible methodology. ## Product overview TES is built by Pentatonic. The primary product surface is a proxy at llm.api.pentatonic.com that sits between your application and your LLM provider. Same SDK, same model, same response shape — the only diff is one environment variable. What the proxy does, per request: 1. **Intercept** — receives an unmodified Anthropic / OpenAI / MiniMax / OpenAI-compatible request. 2. **Retrieve** — fetches the context the model would have re-derived this turn (files, prior memory, tool outputs) from your retrieval source. 3. **Inject preamble** — prepends the retrieved context so the model doesn't need to re-derive it. 4. **Forward** — forwards the (now smaller) request to the chosen upstream and returns the response unchanged. ### Pricing (per-token) - **Free**: 1M proxied input tokens / month. No credit card. - **Pro**: $0.50 per 1M input tokens proxied. $20/mo minimum. - **Enterprise**: $0.30 per 1M at volume. Annual commit. $1k/mo minimum. Reference rate: Anthropic charges $3/1M for Sonnet input direct. TES Pro per-token rate is roughly half that, before the compression saving. Customer wins twice (lower per-token rate + fewer tokens per turn) and can audit both wins by comparing invoices. ### What TES does not do - Does not swap the model. - Does not read responses. - Does not change response shape. - Does not make tool calls unless the customer enables retrieval policies. Per-request opt-out: send `X-TES-Mode: passthrough` and TES forwards to upstream untouched. --- ## Internal surface — GraphQL API (powers the memory layer) TES also exposes a GraphQL API that powers the memory layer behind the proxy. Customers don't integrate with it directly — they use the proxy. This section is retained for existing integrations, documentation completeness, and developer reference. It provides: - Domain modelling over Things, Holders, Locations, Products, Shipments, Payments - AI-powered enrichment (vision, pricing, valuation) from uploaded images - Vector similarity search across the indexed catalogue - Audit records for regulatory compliance (EU AI Act) - OAuth 2.0 authentication with fine-grained permissions ## API Details - **Endpoint:** `POST /api/graphql` - **Authentication:** OAuth 2.0 Bearer Token - **Required Headers:** - `Content-Type: application/json` - `Authorization: Bearer ` - `X-Client-Id: ` - **Architecture:** Event-sourced. All mutations emit events processed asynchronously (~500ms projection latency). ## Entity Reference ### Thing An entity tracked through its lifecycle — physical product, digital asset, subscription, or any trackable item. | Field | Type | Description | |-------|------|-------------| | id | String | Primary key | | name | String | Item name | | product_id | String | FK to Product | | holder_id | String | FK to Holder (current custodian) | | holder_type | Enum | customer, warehouse, store, carrier, processor, manufacturer, unknown | | location_id | String | FK to Location (current location) | | current_stage | Enum | One of 26 lifecycle stages | | current_status | String | Detailed status within stage | | condition | String | Item condition | | quantity | Number | Quantity | | unit | String | Unit of measure | | is_available_for_sale | Boolean | Whether listed for resale | | media | Object | { images: [{ url, r2_key, type, captured_at }] } | | vision | Object | AI-detected: { description, brand, model, colorway, category, condition: { grade, score, notes } } | | pricing | Object | Market data: { currency, market_low, market_mid, market_high, confidence } | | valuation | Object | Resale estimate: { currency, estimated_value: { low, mid, high }, confidence, model_version } | | current_condition | Object | { grade, score, notes, assessed_at } | | current_value_profile | Object | { currency, estimated_value: { low, mid, high }, confidence } | | status_history | Array | [{ parent_status, status, timestamp, holder_id, holder_type, location_id, value, currency, data }] | | holder | Object | Resolved holder: { id, name, type, email } | | location | Object | Resolved location: { id, name, type } | | product | Object | Resolved product: { id, name, brand, model } | | created_at | DateTime | Creation timestamp | | updated_at | DateTime | Last update timestamp | ### Holder An entity that holds things. | Field | Type | Description | |-------|------|-------------| | id | String | Primary key | | name | String | First name or org name | | last_name | String | Last name (for individuals) | | email | String | Contact email | | phone | String | Contact phone | | type | Enum | customer, warehouse, store, carrier, processor, manufacturer, unknown | | address | Object | { city, state, postal_code, country } | | data | JSON | Custom fields | | created_at | DateTime | Creation timestamp | | updated_at | DateTime | Last update timestamp | ### Location A physical place. | Field | Type | Description | |-------|------|-------------| | id | String | Primary key | | name | String | Location name | | type | String | warehouse, store, processing_center, distribution_center | | description | String | Description | | address | Object | { address_line1, address_line2, street, city, state, postal_code, country } | | coordinates | Object | { latitude, longitude } | | timezone | String | IANA timezone | | email | String | Contact email | | phone | String | Contact phone | | data | JSON | Custom fields | | created_at | DateTime | Creation timestamp | | updated_at | DateTime | Last update timestamp | ### Product A product in the catalog. Things are instances of products. | Field | Type | Description | |-------|------|-------------| | id | String | Primary key | | name | String | Product name | | brand | String | Brand name | | manufacturer | String | Manufacturer | | description | String | Full description | | short_description | String | Short description | | category | String | Product category | | tags | String[] | Searchable tags | | features | String[] | Product features | | sku | String | SKU code | | media | JSON | Media references | | data | JSON | Custom fields | | created_at | DateTime | Creation timestamp | | updated_at | DateTime | Last update timestamp | ### Shipment Logistics tracking for a thing. | Field | Type | Description | |-------|------|-------------| | id | String | Primary key | | thing_id | String | FK to Thing | | provider | String | Shipping provider (FedEx, UPS, etc.) | | tracking_number | String | Tracking number | | type | String | Shipment type | | status | String | Current status | | cost | Number | Shipping cost | | currency | String | Cost currency | | label_url | String | Shipping label URL | | estimated_delivery | DateTime | Expected delivery | | delivered_at | DateTime | Actual delivery | | status_history | Array | [{ status, timestamp, location, notes, data }] | | data | JSON | Custom fields | ### Payment Financial transaction linked to a thing. | Field | Type | Description | |-------|------|-------------| | id | String | Primary key | | thing_id | String | FK to Thing | | type | String | Payment type (prepay, COD, refund) | | status | String | pending, processing, completed, failed, refunded | | amount | Decimal | Payment amount | | currency | String | Payment currency | | external_reference | String | External payment ID | | recipient_id | String | Recipient holder ID | | provider | String | Payment provider | | processed_at | DateTime | Processing timestamp | | expires_at | DateTime | Expiration timestamp | | status_history | Array | [{ status, timestamp, failure_reason, data }] | | data | JSON | Custom fields | ### Event Immutable audit log entry. | Field | Type | Description | |-------|------|-------------| | id | String | Primary key | | entityId | String | ID of affected entity | | entityType | String | Type of entity | | eventType | String | Type of event (THING_CREATED, STATUS_CHANGED, etc.) | | timestamp | DateTime | When the event occurred | | payload | JSON | Event data | | source | String | Event source | | clientId | String | Client that triggered the event | | createdAt | DateTime | When stored | ## Complete Query Reference ### Things ```graphql # List things with pagination query { things(limit: 20, offset: 0) { items { id name current_stage holder_id location_id } totalCount hasMore } } # Get single thing with all fields query { thing(id: "thing_123") { id name current_stage vision { brand model condition { grade score } } pricing { market_mid currency } } } # Get thing's raw event history query { thingEvents(id: "thing_123") { id events eventCount } } # Count things by lifecycle stage query { thingsCountByStage(stages: ["sold", "returned", "recycled"]) { stage count } } ``` ### Holders ```graphql query { holders(limit: 20, offset: 0) { items { id name type email } totalCount hasMore } } query { holder(id: "holder_123") { id name last_name email phone type address { city country } } } query { holderEvents(id: "holder_123") { id events eventCount } } ``` ### Locations ```graphql query { locations(limit: 20, offset: 0) { items { id name type address { city country } } totalCount hasMore } } query { location(id: "loc_123") { id name type address { street city state postal_code country } coordinates { latitude longitude } } } ``` ### Products ```graphql query { products(limit: 20, offset: 0) { items { id name brand category sku tags } totalCount hasMore } } query { product(id: "prod_123") { id name brand manufacturer description category tags features sku } } query { uniqueProductTags } ``` ### Vector Search ```graphql # Search by natural language query { searchThings(input: { query: "vintage leather jacket in good condition", min_score: 0.7, limit: 10 }) { items { score thing { id name vision { brand category } pricing { market_mid currency } } } total_candidates search_type query_embedding_generated }} # Search by similar thing query { searchThings(input: { similar_to_thing_id: "thing_123", min_score: 0.8, limit: 5 }) { items { score thing { id name } } }} # Search products query { searchProducts(input: { query: "sustainable water bottle", limit: 10 }) { items { id score name brand category } }} ``` ### Shipments ```graphql query { shipments(limit: 20, offset: 0) { items { id thing_id provider tracking_number status cost currency } totalCount hasMore } } query { shipment(id: "ship_123") { id thing_id provider tracking_number status status_history { status timestamp location notes } } } query { shipmentAnalytics { total_count by_provider by_status total_cost average_cost } } ``` ### Payments ```graphql query { payments(limit: 20, offset: 0) { items { id thing_id type status amount currency } totalCount hasMore } } query { payment(id: "pay_123") { id thing_id type status amount currency status_history { status timestamp failure_reason } } } query { paymentAnalytics { total_count total_amount by_type by_status by_currency average_amount } } ``` ### Events ```graphql query { events(filter: { entityType: "thing", eventType: "STATUS_CHANGED" }, limit: 50) { items { id entityId eventType timestamp payload } totalCount hasMore } } query { event(id: "evt_123") { id entityId entityType eventType timestamp payload source } } query { eventsByEntity(entityId: "thing_123", limit: 20) { id eventType timestamp payload } } query { eventStats { totalCount byEntityType { type count } byEventType { type count } last24Hours last7Days } } ``` ### Edges (Relationship History) ```graphql query { edges { entityId relatedEntityId relatedEntityType relationship } } query { edgeHistory(entityId: "thing_123", relationship: "holder_id", limit: 10) { items { id relatedEntityId relatedEntityType timestamp } totalCount hasMore } } ``` ## Complete Mutation Reference ### Things ```graphql mutation { createThing(input: { name: "Blue Leather Jacket", current_stage: "captured", holder_id: "holder_123", holder_type: "customer" }) { success message eventId } } mutation { updateThing(id: "thing_123", input: { name: "Updated Name", condition: "good" }) { success message eventId } } mutation { addThingStatus(id: "thing_123", input: { parent_status: "inspecting", status: "condition_check" }) { success message eventId } } mutation { transferThing(id: "thing_123", input: { holder_id: "holder_456", holder_type: "warehouse" }) { success message eventId } } mutation { changeThingLocation(id: "thing_123", input: { location_id: "loc_789" }) { success message eventId } } mutation { uploadThingImage(id: "thing_123", input: { url: "https://example.com/image.jpg", type: "photo" }) { success message eventId } } mutation { deleteThing(id: "thing_123") { success message eventId } } ``` ### Holders ```graphql mutation { createHolder(input: { name: "Sarah", last_name: "Johnson", email: "sarah@example.com", type: "customer", address: { city: "London", country: "UK" } }) { success message holder_id } } mutation { updateHolder(id: "holder_123", input: { phone: "+44 7700 900000" }) { success message holder_id } } mutation { deleteHolder(id: "holder_123") { success message eventId } } ``` ### Locations ```graphql mutation { createLocation(input: { name: "Central Warehouse", type: "warehouse", address: { city: "Birmingham", country: "UK" } }) { success message eventId } } mutation { updateLocation(id: "loc_123", input: { name: "Updated Warehouse" }) { success message eventId } } mutation { deleteLocation(id: "loc_123") { success message eventId } } ``` ### Products ```graphql mutation { createProduct(input: { name: "Eco Water Bottle", brand: "GreenCo", category: "containers", sku: "ECO-500", tags: ["sustainable", "reusable"] }) { success message product_id } } mutation { updateProduct(id: "prod_123", input: { description: "Updated description" }) { success message product_id } } mutation { deleteProduct(id: "prod_123") { success message } } ``` ### Shipments ```graphql mutation { createShipment(input: { thing_id: "thing_123", provider: "FedEx", tracking_number: "1234567890", type: "return" }) { success message shipment_id eventId } } mutation { updateShipmentStatus(id: "ship_123", input: { status: "in_transit", location: "Birmingham Hub", notes: "Package scanned" }) { success message shipment_id eventId } } mutation { deleteShipment(id: "ship_123") { success message eventId } } ``` ### Payments ```graphql mutation { createPayment(input: { thing_id: "thing_123", type: "refund", amount: 49.99, currency: "GBP", recipient_id: "holder_123" }) { success message payment_id eventId } } mutation { updatePaymentStatus(id: "pay_123", input: { status: "completed" }) { success message payment_id eventId } } mutation { deletePayment(id: "pay_123") { success message eventId } } ``` ## Lifecycle Stages (26) | Phase | Stages | |-------|--------| | Production | manufactured, sourced | | Inventory | in_stock, in_transit, delivered | | Customer | sold, in_use | | Capture | captured, identified, valued | | Return | returned, received | | Processing | processing, inspecting, refurbishing, repairing | | Outcomes | processed, certified, listed, resold | | End States | recycled, donated, disposed | | Problems | issue, rejected, lost | ## AI Enrichment Pipeline When an image is uploaded to a thing via `uploadThingImage`, these stages run automatically: 1. **Vision Analysis** — Identifies brand, model, colorway, category, and condition (grade A-F with score 0-100) 2. **Market Pricing** — Returns market_low, market_mid, market_high with confidence (0-1) 3. **Valuation** — Estimates resale value range with confidence score and model version 4. **Name Generation** — Auto-generates descriptive name from vision data 5. **Text Embedding** — Creates 1024-dimensional BGE-M3 vector for semantic search 6. **Product Matching** — Auto-links to existing catalog product (threshold 0.8) or creates new Processing is optimistic: failures are logged but never block event storage. ## Authentication ### OAuth 2.0 Flows **Client Credentials (server-to-server):** ``` POST /oauth/token Content-Type: application/x-www-form-urlencoded grant_type=client_credentials&client_id=&client_secret= ``` **Authorization Code with PKCE (user-facing):** ``` GET /oauth/authorize?response_type=code&client_id=&redirect_uri=&code_challenge=&code_challenge_method=S256 POST /oauth/token grant_type=authorization_code&code=&client_id=&redirect_uri=&code_verifier= ``` **Token format:** `tes__` ### Permissions Format: `action:entity:scope` - Actions: create, view, update, delete, transfer, changeLocation - Entities: customer, takeback, thing, payment, shipping - Scopes: all (all records), self (own records only) ## SDKs ### JavaScript / TypeScript ```bash npm install @pentatonic-ai/ai-agent-sdk ``` ```javascript import { TESClient } from '@pentatonic-ai/ai-agent-sdk'; const tes = new TESClient({ apiKey: 'tes_...', clientId: 'your-client-id', }); // Auto-wrap any LLM client — OpenAI, Anthropic, or Workers AI const ai = tes.wrap(new OpenAI()); // Every call is now automatically tracked as a TES event const res = await ai.chat.completions.create({ ... }); // Manual session for full control const session = tes.session({ sessionId: 'conv-123' }); await session.emitChatTurn({ userMessage, assistantResponse }); ``` - npm: https://www.npmjs.com/package/@pentatonic-ai/ai-agent-sdk - License: MIT ### Python ```bash pip install pentatonic-ai-agent-sdk ``` ```python from pentatonic_ai_agent_sdk import TESClient tes = TESClient( api_key="tes_...", client_id="your-client-id", ) # Auto-wrap any LLM client ai = tes.wrap(openai_client) ``` - PyPI: https://pypi.org/project/pentatonic-ai-agent-sdk/ - License: MIT ### Source Code - GitHub: https://github.com/Pentatonic-Ltd/ai-agent-sdk - SDK docs: https://thingeventsystem.ai/sdk - Claude Code plugin: https://thingeventsystem.ai/sdk/claude-code ## MCP Integration TES provides Model Context Protocol tools for AI assistants: ```json { "mcpServers": { "tes": { "command": "npx", "args": ["-y", "@anthropic-ai/mcp-remote", "https://tes.pentatonic.com/sse"], "env": { "TES_API_TOKEN": "" } } } } ``` Available tools: list_things, get_thing, create_thing, update_thing, add_thing_status, transfer_ownership, change_location, search_things, list_holders. ## Links - Website: https://thingeventsystem.ai - Documentation: https://thingeventsystem.ai/docs - GraphQL Playground: https://thingeventsystem.ai/graphql - E2E Demo: https://thingeventsystem.ai/e2e - Vector Search: https://thingeventsystem.ai/search - Status: https://thingeventsystem.ai/status - npm (JS/TS): https://www.npmjs.com/package/@pentatonic-ai/ai-agent-sdk - PyPI (Python): https://pypi.org/project/pentatonic-ai-agent-sdk/ - GitHub: https://github.com/Pentatonic-Ltd/ai-agent-sdk - SDK docs: https://thingeventsystem.ai/sdk - Discord: https://discord.gg/QZJe9FtkWj - Parent company: https://pentatonic.com