Beaver API

Beaver is Almond's production system: parts, BOMs, inventory, purchase orders, builds, and assembly instructions. Everything in the UI is available over a JSON REST API so any coding agent or script can read and write the same data.

  • Base URL: https://beaver.almond.bot/api/v1
  • Auth: every request needs Authorization: Bearer bvr_... (ask a teammate for the key, or mint a new one in the api_keys table — store the sha256 hex of the key in key_hash).
  • Machine-readable index: GET /api/v1 lists every endpoint.
  • IDs: part endpoints accept either the numeric id or the IPN (e.g. ALM-000001).

Quick start (Python)

import requests

BASE = "https://beaver.almond.bot/api/v1"
H = {"Authorization": "Bearer bvr_..."}

# find a part
parts = requests.get(f"{BASE}/parts", params={"q": "wrist"}, headers=H).json()["parts"]

# how many Axols can we build, and what's short for 10?
bom = requests.get(f"{BASE}/parts/ALM-000001/bom", params={"quantity": 10}, headers=H).json()
print(bom["buildable"], [r for r in bom["availability"] if r["short"] > 0])

# receive 50 screws (inventory +50)
requests.post(f"{BASE}/parts/ALM-000042/inventory", json={"delta": 50, "reason": "restock"}, headers=H)

# set an absolute count after a shelf audit
requests.post(f"{BASE}/parts/ALM-000042/inventory", json={"quantity": 137}, headers=H)

# build 2 wrist assemblies: consumes the BOM, adds 2 finished units
b = requests.post(f"{BASE}/builds", json={"part_id": 928, "quantity": 2}, headers=H).json()["build"]
requests.post(f"{BASE}/builds/{b['id']}/complete", headers=H)

Core concepts

Parts

Every physical item — products, subassemblies, fasteners, consumables. Each part has an IPN (ALM-######), a category, a picture, and live on_hand / on_order counts. on_hand is never written directly: it is the sum of inventory events.

BOMs

bom_items link a parent assembly to child parts with a quantity. GET /parts/:id/bom returns direct children, the fully exploded flat BOM, per-child availability for a target quantity, and buildable (how many the current stock supports).

Inventory

Append-only inventory_events move stock. Three ways stock changes:

  1. Manual / count — POST /parts/:id/inventory with delta (relative) or quantity (absolute).
  2. Receiving an order — POST /orders/:id/receive adds the order quantity.
  3. Completing a build — POST /builds/:id/complete subtracts the BOM children and adds the finished assemblies.

Orders (shopping list)

An order is a line for one part: requested → ordered → shipped → received (or cancelled). Open orders count toward the part's on_order. The Orders page is the shopping list; GET /orders?status=requested is "still to buy".

Builds

POST /builds {part_id, quantity} starts a build of an assembly. GET /builds/:id shows required parts and shortages before you commit. POST /builds/:id/complete performs the inventory moves in one transaction.

Assembly instructions (processes)

A process has ordered steps; each step has markdown and picture URLs. GET /processes/:id returns steps with resolved image_urls. Add pictures with POST /processes/:id/steps/:stepId/image (multipart, field image).

Video → instructions pipeline

  1. POST /video-jobs/upload-url {filename} → returns path + upload_url
  2. PUT the raw video bytes to upload_url (Content-Type: video/mp4)
  3. POST /video-jobs {video_path: path, part_id?, hints?}
  4. POST /video-jobs/:id/process — blocks for a few minutes while frames are extracted and a vision model writes the steps. Returns process_id.
  5. Review the draft at https://beaver.almond.bot/instructions/{process_id} and publish.

If step 4 fails with a rate-limit error (the AI Gateway free tier is tightly rate-limited), wait a few minutes and re-run it — the job is safely retryable.

Conventions (ported from Anvil)

  • IPN prefix is ALM- with a six-digit sequence; new parts auto-assign the next one if you omit ipn.
  • Categories: Product, Subassembly, Electronics, Fasteners, Wiring, Structure, Mechanical, Consumable, Tooling, Material. Match existing spellings.
  • manufacturer is the actual maker; distributors (McMaster-Carr, Amazon, DigiKey…) belong in supplier links with URL/SKU/price.
  • procurement_type: OTS (off the shelf), MTS (made to spec), Make (built here).
  • Fastener naming: BH-TX-M3-08 (head-drive-size-length), no leading zeros in descriptions.

Direct database access

The data lives in Supabase Postgres (project jukbsqdjwfmjhycdvjkw). Agents with Supabase access can query it directly; the REST API above is preferred for writes because it goes through the same domain functions (adjust_inventory, complete_build, receive_order, set_inventory_count) that keep counts consistent.