Gripp

Developers

Public API

Gripp matches excess inventory to buyer requirements worldwide. A buyer key can search listings and post requirements. A supplier key can publish stock. Buyer responses never include the supplier's identity or floor price.

Base URL https://grippai-production.up.railway.app/v1. Interactive reference at /v1/docs. Send Authorization: Bearer or X-Api-Key. Repeat a POST safely with Idempotency-Key.

Endpoints

Webhooks

Register match.created, requirement.matched, listing.created, and quote.updated. Gripp signs the raw body with HMAC-SHA256 in X-Gripp-Signature. Failed deliveries retry with exponential backoff. The delivery log supports replay and a test ping.

Agents

Point an MCP client at https://grippai-production.up.railway.app/v1/mcp. Tools: search_inventory, post_requirement, list_matches, request_quote.

TypeScript

export class Gripp {
  constructor(
    private apiKey: string,
    private baseUrl = "https://grippai-production.up.railway.app/v1",
  ) {}

  private async request(method: string, path: string, body?: unknown, idempotencyKey?: string) {
    const headers: Record<string, string> = {
      Authorization: `Bearer ${this.apiKey}`,
      Accept: "application/json",
    }
    if (body !== undefined) headers["Content-Type"] = "application/json"
    if (idempotencyKey) headers["Idempotency-Key"] = idempotencyKey
    const response = await fetch(`${this.baseUrl}${path}`, {
      method,
      headers,
      body: body === undefined ? undefined : JSON.stringify(body),
    })
    const payload = await response.json()
    if (!response.ok) throw new Error(payload?.error?.message || response.statusText)
    return payload
  }

  searchInventory(query: string, category?: string) {
    const params = new URLSearchParams({ q: query })
    if (category) params.set("category", category)
    return this.request("GET", `/listings?${params}`)
  }

  postRequirement(
    requirement: { title: string; category: string; quantity: number },
    idempotencyKey?: string,
  ) {
    return this.request("POST", "/requirements", requirement, idempotencyKey)
  }
}

Python

import json
from urllib.request import Request, urlopen


class GrippClient:
    def __init__(self, api_key: str, base_url: str = "https://grippai-production.up.railway.app/v1"):
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")

    def _request(self, method: str, path: str, payload=None, idempotency_key: str | None = None):
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Accept": "application/json",
        }
        data = None
        if payload is not None:
            data = json.dumps(payload).encode()
            headers["Content-Type"] = "application/json"
        if idempotency_key:
            headers["Idempotency-Key"] = idempotency_key
        request = Request(self.base_url + path, data=data, headers=headers, method=method)
        with urlopen(request) as response:
            return json.load(response)

    def search_inventory(self, query: str, category: str | None = None):
        params = f"q={query}"
        if category:
            params += f"&category={category}"
        return self._request("GET", f"/listings?{params}")

    def post_requirement(self, requirement: dict, idempotency_key: str | None = None):
        return self._request("POST", "/requirements", requirement, idempotency_key)