aura-labs.ai

Beacon SDK Documentation

Build selling agents that connect your inventory to the AURA ecosystem. Beacons are server-side agents that register with AURA Core, handle buyer sessions, submit offers, and manage transaction lifecycles.

What is a Beacon?

A Beacon is a selling agent that:

Beacons integrate into seller systems: e-commerce platforms, ERP systems, inventory management, booking systems, and marketplace integrations.

Quick Start

Installation

npm install @aura-labs/beacon

Basic Usage

import { createBeacon } from '@aura-labs/beacon';

// Create beacon instance
const beacon = createBeacon({
  externalId: 'my-store-001',      // Your internal identifier
  name: 'My Store',
  description: 'Electronics retailer',
  endpointUrl: 'https://mystore.com/webhook',  // Receives transaction webhooks
  capabilities: ['retail', 'shipping'],
  coreUrl: 'https://core.aura-labs.io',
  pollIntervalMs: 5000               // Poll every 5 seconds
});

// Register with AURA Core
const { beaconId } = await beacon.register();
console.log('Registered with beaconId:', beaconId);

// Handle incoming sessions
beacon.onSession(async (session, beacon) => {
  const { sessionId, status, intent, constraints, createdAt } = session;

  // intent.raw: original buyer text
  // intent.parsed: structured intent { keywords }
  // constraints: { categories } — buyer constraints are redacted for information asymmetry

  // Use interpretIntent for smart catalog matching (recommended)
  const result = await beacon.interpretIntent(intent.raw, myCatalog);

  if (result.matches.length > 0) {
    const topMatch = result.matches[0];
    const offer = {
      product: topMatch.item,
      unitPrice: calculatePrice(topMatch.item),
      quantity: 1,
      currency: 'USD',
      deliveryDate: calculateDelivery(),
      terms: 'Standard terms apply'
    };

    await beacon.submitOffer(sessionId, offer);
  }
});

// Apply business rules
beacon.registerPolicies({
  minPrice: 10,
  maxQuantityPerOrder: 100,
  maxDeliveryDays: 30,
  deliveryRegions: ['US', 'CA', 'UK']
});

// Optional: Pre-offer validation
beacon.beforeOffer(async (session, offer) => {
  // Validate or modify offer before submission
  if (offer.unitPrice < 5) {
    throw new Error('Price too low');
  }
  return offer;  // Return modified offer or undefined to block
});

// Track accepted offers
beacon.onOfferAccepted(async (transactionData) => {
  console.log('Offer accepted, transaction:', transactionData.transactionId);
});

// Start polling for sessions
await beacon.startPolling();

// Graceful shutdown
process.on('SIGINT', async () => {
  await beacon.stopPolling();
});

NLP Intent Interpretation

Beacons can interpret incoming buyer intents against their product catalog using @aura-labs/nlp — the shared NLP module used across the entire AURA pipeline. This is the recommended way to evaluate sessions, replacing simple string matching with structured category detection and keyword-scored catalog matching.

How It Works

Intent interpretation is Layer 3 of the AURA three-layer NLP architecture:

Layer 1: @aura-labs/nlp      — Shared module (category detection, completeness checking)
Layer 2: Scout SDK + Core    — Completeness gate + authoritative parsing
Layer 3: Beacon SDK           — Domain-specific catalog matching (this layer)

The Beacon performs presence detection only — it does not perform semantic authority over intent meaning. Core retains semantic authority per the Neutral Broker architecture (NEUTRAL_BROKER.md Property 1).

beacon.interpretIntent(intentText, catalog, options?)

Interpret a buyer’s intent against your product catalog:

const catalog = [
  { name: 'Ergonomic Keyboard', sku: 'KB-ERG-001', category: 'keyboards', tags: ['ergonomic', 'wireless'] },
  { name: 'Gaming Mouse', sku: 'MS-GAM-001', category: 'mice', tags: ['gaming', 'wireless'] },
  { name: 'Ultra-Wide Monitor', sku: 'MN-UW-001', category: 'monitors', tags: ['ultrawide', '34-inch'] },
];

const result = await beacon.interpretIntent(
  'I need 50 ergonomic keyboards under $5000',
  catalog,
);

Returns:

{
  matches: [
    { item: { name: 'Ergonomic Keyboard', ... }, score: 60, matchedOn: ['name', 'category', 'tags'] }
  ],
  confidence: 0.45,    // Overall intent completeness (0-1)
  categories: {         // Per-category presence detection from @aura-labs/nlp
    what: { present: true },
    how_many: { present: true },
    how_much_cost: { present: true },
    // ... 8 categories total (4 Tier 1 + 4 Tier 2)
  },
  suggestions: [        // Clarification questions for missing categories
    'What specific features are you looking for?'
  ]
}

Parameters:

Scoring: Items are scored by keyword overlap: +20 per name match, +15 per category match, +10 per tag match (capped at 100). Common stop words are filtered out. Partial matches are supported (e.g., “keyboard” matches “keyboards”).

Usage in Session Handlers

Replace simple string matching with interpretIntent() for smarter offer decisions:

beacon.onSession(async (session) => {
  const result = await beacon.interpretIntent(session.intent.raw, myCatalog);

  if (result.matches.length > 0) {
    const topMatch = result.matches[0];
    await beacon.submitOffer(session.sessionId, {
      product: topMatch.item,
      unitPrice: topMatch.item.price,
      quantity: 1,
    });
  }
});

Standalone Function

For use outside the Beacon class (e.g., testing, pre-processing):

import { interpretIntent } from '@aura-labs/beacon';

const result = await interpretIntent('I need widgets', myCatalog);

Graceful Degradation

interpretIntent() never throws on provider failure — if the LLM provider is unavailable, it falls back to regex-only category detection and still performs catalog matching. Provider errors are logged via activity events.

Core Concepts

Registration

Beacons register once with AURA Core:

const beacon = createBeacon({
  externalId: 'my-store-001',        // Your internal identifier
  name: 'My Store',                  // Display name
  description: 'What you sell',      // Brief description
  endpointUrl: 'https://mystore.com/webhook',  // Webhook receiver
  capabilities: ['retail', 'shipping', 'drop-shipping'],  // What you can do
  coreUrl: 'https://core.aura-labs.io',
  pollIntervalMs: 5000
});

const { beaconId } = await beacon.register();
// Returns beaconId (UUID) - use this to identify your beacon
// Future: Registration will also return Ed25519 keypair for request signing

What happens:

Session Handling

Sessions represent buyer intent. Your beacon polls for them:

beacon.onSession(async (session, beacon) => {
  // session structure:
  // {
  //   sessionId: "uuid",
  //   status: "market_forming",
  //   intent: {
  //     raw: "I need 500 industrial widgets under $50000",
  //     parsed: { keywords: ['need', '500', 'industrial', 'widgets'] }
  //   },
  //   constraints: {
  //     categories: ["widgets", "industrial"]   // Buyer constraints are redacted
  //   },
  //   createdAt: "2026-03-03T10:00:00Z"
  // }

  // Use interpretIntent for smart matching (recommended)
  const result = await beacon.interpretIntent(session.intent.raw, myCatalog);
  if (result.matches.length > 0) {
    await beacon.submitOffer(session.sessionId, buildOffer(result.matches[0]));
  }
});

Polling:

// Automatically polls GET /v1/beacons/sessions every pollIntervalMs
await beacon.startPolling();

// Deduplicates sessions via internal Set
// Calls onSession handler for each new unique session
// Stops polling
await beacon.stopPolling();

Offer Submission

Submit an offer to a session:

const offer = {
  product: 'Wireless Headphones Pro',      // Your product name/identifier
  unitPrice: 89.99,                        // Price per unit
  quantity: 1,                             // Quantity available
  currency: 'USD',
  deliveryDate: '2026-03-10',              // When you can deliver
  terms: 'Standard return policy applies', // Optional terms
  metadata: { sku: 'SKU-123', color: 'black' }  // Optional metadata
};

await beacon.submitOffer(sessionId, offer);
// POST to /v1/sessions/:sessionId/offers
// Returns offer confirmation

Validation flow:

  1. Offer passes through beforeOffer validators (if registered)
  2. Offer passes through policy validators (if registered)
  3. Offer submitted to Core

If any validator throws or returns undefined, offer is blocked.

Merchant Integration Hooks

beforeOffer: Pre-Offer Validation Middleware

Validate or modify offers before submission:

beacon.beforeOffer(async (session, offer) => {
  // Validators run sequentially (chainable)
  // Return modified offer, undefined to block, or throw to reject

  // Example: Enforce minimum margin
  if (offer.unitPrice < 50) {
    throw new Error(`Price ${offer.unitPrice} below cost`);
  }

  // Example: Modify offer based on session
  if (session.constraints.maxDeliveryDays < 3) {
    offer.deliveryDate = calculateExpressDelivery();
    offer.terms = 'Express delivery applies';
  }

  return offer;  // Return to proceed
});

// Chain multiple validators
beacon
  .beforeOffer(validateMinimumPrice)
  .beforeOffer(validateInventory)
  .beforeOffer(addDynamicDiscount);

registerPolicies: Business Rules

Declare business rules. SDK auto-validates offers:

beacon.registerPolicies({
  minPrice: 10,                    // Minimum unit price
  maxQuantityPerOrder: 100,        // Max units per offer
  maxDeliveryDays: 30,             // Maximum delivery time
  deliveryRegions: ['US', 'CA', 'UK']  // Where you ship
});

What happens:

onOfferAccepted: Offer Committed

Called when a buyer commits to your offer:

beacon.onOfferAccepted(async (transactionData) => {
  // {
  //   transactionId: "uuid",
  //   sessionId: "uuid",
  //   offer: { product, unitPrice, quantity, ... },
  //   committedAt: "2026-03-03T10:05:00Z"
  // }

  console.log('Order confirmed:', transactionData.transactionId);
  // Reserve inventory, create order record, etc.
});

onTransactionUpdate: Track Status Changes

Called for any transaction status change:

beacon.onTransactionUpdate(async (event) => {
  // {
  //   transactionId: "uuid",
  //   status: "committed" | "shipped" | "delivered" | "fulfilled" | "completed",
  //   timestamp: "2026-03-03T10:05:00Z",
  //   metadata: { ... }
  // }

  if (event.status === 'shipped') {
    console.log('Order shipped:', event.transactionId);
  }
});

Fulfillment Tracking

Update fulfillment status as you process the order:

// When you ship the order
await beacon.updateFulfillment(transactionId, {
  fulfillmentStatus: 'shipped',
  fulfillmentReference: 'TRACK-123456',  // Tracking number
  metadata: { carrier: 'FedEx' }
});

// When delivered
await beacon.updateFulfillment(transactionId, {
  fulfillmentStatus: 'delivered',
  metadata: { deliveredAt: '2026-03-10T14:30:00Z' }
});

// GET transaction details
const transaction = await beacon.getTransaction(transactionId);
// {
//   transactionId: "uuid",
//   sessionId: "uuid",
//   status: "delivered",
//   offer: { ... },
//   fulfillment: { status: "delivered", reference: "TRACK-123456" },
//   createdAt: "2026-03-03T10:05:00Z",
//   updatedAt: "2026-03-10T14:30:00Z"
// }

Transaction Lifecycle

Transactions follow this state machine:

committed → shipped → delivered → fulfilled → completed
           (your action) (your action) (auto)     (auto)
                                    ↓
                              (buyer pays + delivered)

States:

Webhooks:

Core sends webhooks to your endpointUrl at each state change:

// Your endpoint receives:
POST https://mystore.com/webhook
{
  transactionId: "uuid",
  status: "shipped",
  timestamp: "2026-03-10T10:00:00Z",
  offer: { ... }
}

Handle webhooks to trigger fulfillment workflows, accounting updates, etc.

API Reference

Factory Function

const beacon = createBeacon(config)
Config Type Description
externalId string Your internal beacon identifier
name string Display name for your beacon
description string What the beacon does/sells
endpointUrl string HTTPS URL to receive webhooks
capabilities string[] Your capabilities: retail, shipping, drop-shipping, etc.
coreUrl string AURA Core URL (default: production)
pollIntervalMs number Session polling interval in milliseconds (default: 5000)

Methods

Method Signature Description
register() async () => { beaconId } Register beacon with Core, returns beaconId
startPolling() async () => void Start polling for new sessions
stopPolling() async () => void Stop polling
submitOffer() async (sessionId, offer) => void Submit offer to a session
interpretIntent() async (intentText, catalog, options?) => InterpretResult Interpret buyer intent against product catalog using NLP
beforeOffer() (validator) => beacon Register pre-offer validator (chainable)
registerPolicies() (rules) => beacon Register business policies (chainable)
onSession() (handler) => beacon Register session handler (chainable)
onOfferAccepted() (handler) => beacon Register offer-accepted handler (chainable)
onTransactionUpdate() (handler) => beacon Register transaction-update handler (chainable)
updateFulfillment() async (transactionId, update) => void Update fulfillment status
getTransaction() async (transactionId) => transaction Get transaction details

Hooks & Callbacks

Hook Signature Description
onSession (session, beacon) => Promise<void> Called for each new session during polling
beforeOffer (session, offer) => Promise<offer \| undefined> Validator runs before submitOffer, can modify or block
onOfferAccepted (transactionData) => Promise<void> Called when offer is committed
onTransactionUpdate (event) => Promise<void> Called for any transaction status change

Data Structures

Session:

{
  sessionId: string,
  status: 'market_forming' | 'committed' | 'cancelled' | 'expired',
  intent: {
    raw: string,           // Original buyer text
    parsed: {
      keywords: string[]   // Extracted keywords
    }
  },
  constraints: {
    categories: string[]   // Buyer constraints are redacted — only categories visible
  },
  createdAt: string        // ISO timestamp
}

Offer:

{
  product: string,         // Your product name/identifier
  unitPrice: number,       // Price per unit
  quantity: number,        // Quantity available
  totalPrice?: number,     // Optional: computed automatically if omitted
  currency?: string,       // Default: 'USD'
  deliveryDate: string,    // Date you can deliver (ISO format)
  terms?: string,          // Optional terms/conditions
  metadata?: object        // Optional custom data
}

Transaction:

{
  transactionId: string,
  sessionId: string,
  status: 'committed' | 'shipped' | 'delivered' | 'fulfilled' | 'completed',
  offer: Offer,
  fulfillment?: {
    status: 'shipped' | 'delivered',
    reference?: string,    // Tracking number
    metadata?: object
  },
  createdAt: string,
  updatedAt: string
}

Best Practices

Session Evaluation

Offer Submission

Validation Hooks

Fulfillment Tracking

Polling & Performance

Error Handling

beacon.onSession(async (session) => {
  try {
    // Evaluate and submit offer
    if (canFulfill(session)) {
      await beacon.submitOffer(sessionId, offer);
    }
  } catch (error) {
    console.error('Error handling session:', error);
    // Don't re-throw — let polling continue
  }
});

Activity Events

The Beacon SDK tracks operational metrics via an internal activity logger. These events are available via beacon.getActivitySummary():

Event Emitted When Metadata
session.received New session polled sessionId
offer.submitted Offer sent to Core sessionId, offerId
offer.rejected beforeOffer validator blocked offer reason
intent.interpreted interpretIntent() succeeds matchCount, confidence, categoriesDetected, catalogSize
intent.interpretation_failed interpretIntent() provider error error

Activity Summary:

const summary = beacon.getActivitySummary();
// {
//   sessions: { received: 42, ... },
//   offers: { submitted: 18, rejected: 3, ... },
//   interpretations: { total: 42, matched: 35, unmatched: 7, failed: 0 }
// }

Integration Guides

Examples

See Beacon Implementations for:

See Also