Skip to content
cresvaDevelopers

A JavaScript quickstart

GET/api/storefront/sdk/javascript
No keyCallable anonymously. Rate limited by IP.10 requests a minute

Request

bash
curl "https://cresva.ai/api/storefront/sdk/javascript"

Response

Captured from production on 2026-09-16: 200 in 304ms. This is what the endpoint returned, not an example of what it might.

JSON
/**
 * Cresva Storefront SDK for JavaScript/TypeScript
 * Zero dependencies - uses native fetch API
 *
 * Usage:
 *   const cresva = new CresvaClient({ apiKey: "pk_live_...", brandId: "your-brand-id" });
 *   const results = await cresva.search("organic cotton tshirt");
 */

// ---------------------------------------------------------------------------
// Types
//
// Two response families exist:
//   1. POST /query (Agent Query Protocol) returns QueryResponse with rich
//      ProductCard results.
//   2. The GET convenience endpoints (/search, /products, /recommend,
//      /compare) return a simpler { products, meta } envelope with
//      StorefrontProductCard entries.
// ---------------------------------------------------------------------------

/** Rich product card returned by POST /query results. */
export interface ProductCard {
  id: string;
  title: string;
  summary: string;
  description: string;
  price: { amount: number; currency: string; formatted: string };
  availability: { status: string; quantity?: number };
  rating: { score: number; count: number; summary?: string } | null;
  attributes: Record<string, string | string[]>;
  images: { primary: string; thumbnails?: string[] };
  relevance_score: number;
  agent_context: { why_recommended: string; purchase_confidence: number };
  urls: { product: string; purchase: string };
}

/** Compact product card returned by the GET convenience endpoints. */
export interface StorefrontProductCard {
  id: string;
  title: string;
  summary: string;
  price: { amount: number; currency: string; formatted: string };
  availability: "in_stock" | "out_of_stock";
  rating: { score: number; count: number } | null;
  attributes: Record<string, string | string[]>;
  comparison_context: string | null;
  purchase_url: string | null;
  images: string[];
}

/** Meta block on GET convenience endpoint responses. */
export interface StorefrontMeta {
  total: number;
  page: number;
  authority_score: number | null;
  response_time_ms: number;
  data_freshness: string;
}

export interface SearchResponse {
  query: string;
  products: Array<StorefrontProductCard & { relevance: number }>;
  meta: StorefrontMeta;
}

export interface ProductsResponse {
  products: StorefrontProductCard[];
  meta: StorefrontMeta;
}

export interface RecommendResponse {
  source_product: string;
  recommendation_type: string;
  products: Array<
    StorefrontProductCard & {
      relationship: { type: string; strength: number; reasoning: string | null };
    }
  >;
  meta: StorefrontMeta;
}

export interface CompareResponse {
  products: StorefrontProductCard[];
  comparison: {
    products_compared: number;
    highest_rated: { id: string; score: number } | null;
    most_reviewed: { id: string; count: number } | null;
  };
  meta: StorefrontMeta;
}

/**
 * Recommendation relationship types accepted by GET /recommend.
 * Any other value is rejected by the API.
 */
export type RecommendationType =
  | "similar_to"
  | "alternative_to"
  | "pairs_with"
  | "variant_of"
  | "complement"
  | "upgrade_from"
  | "good_for";

export interface QueryResponse {
  protocol_version: string;
  request_id: string;
  meta: {
    brand: string;
    authority_score: number | null;
    total_results: number;
    response_time_ms: number;
    cache_status: string;
  };
  results: ProductCard[];
  pagination: {
    page: number;
    limit: number;
    total_pages: number;
    has_more: boolean;
  };
  query_interpretation: {
    parsed_intent: string;
    applied_filters: Record<string, unknown>;
    search_terms: string[];
  };
}

export interface SearchOptions {
  /** Max results (1 to 30, default 10). */
  limit?: number;
}

// ---------------------------------------------------------------------------
// Error classes
// ---------------------------------------------------------------------------

export class CresvaError extends Error {
  public statusCode: number;
  public requestId?: string;

  constructor(statusCode: numb
 truncated at 4000 characters

Response codes

200Text.
string
401The key does not match any active key.
erroranyTwo shapes exist across this API and that is deliberate rather than untidy. Each route kept the error shape it already used, so an existing client's error handling keeps working. See x-cresva-error-shapes.
403The key is valid but belongs to a different brand than the one in the path. Keys are scoped to one brand and do not travel.
erroranyTwo shapes exist across this API and that is deliberate rather than untidy. Each route kept the error shape it already used, so an existing client's error handling keeps working. See x-cresva-error-shapes.
404No such brand.
erroranyTwo shapes exist across this API and that is deliberate rather than untidy. Each route kept the error shape it already used, so an existing client's error handling keeps working. See x-cresva-error-shapes.
429Over the rate limit.
erroranyTwo shapes exist across this API and that is deliberate rather than untidy. Each route kept the error shape it already used, so an existing client's error handling keeps working. See x-cresva-error-shapes.
503The rate limiter could not be reached, so the request was refused rather than served unmetered. Deliberately not a 429: the caller has done nothing wrong and the fault is ours.
erroranyTwo shapes exist across this API and that is deliberate rather than untidy. Each route kept the error shape it already used, so an existing client's error handling keeps working. See x-cresva-error-shapes.

Generated from the storefront OpenAPI document at growthagents 269d7898b, sha256 047fe4d301258100. Nothing on this page was typed by hand.