Skip to main content

Shop

5 operations under /v1/shop/agent (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.

7 min read
View MarkdownEdit on GitHub

Base URL: https://api.codespar.dev

Every operation below requires a Bearer token. See Authentication.

GET /v1/shop/agent/instruments

GEThttps://api.codespar.dev/v1/shop/agent/instruments

The calling person's cards, masked

Brand and last four of the CALLING person's active cards, as references an assistant can name; choosing one stays the person's act at Pagar. The list belongs to a verified person: a project API key or an OAuth token carries no person and is refused bearer_token_has_no_person (403), whatever owner says. owner is deprecated and ignored: it no longer selects another person's cards (Shop v0.3). The 403's details say why and what to do, with stable keys: reason: "bearer_has_no_person", list_belongs_to: "authenticated_person", owner_param: "deprecated_ignored" and how_to_list (the person's own session on the dashboard; an MCP session bound to a person by login arrives with Shop v0.3 increment 5). A client holding only a project key has no way to list cards until then.

What an assistant can and cannot do. It proposes a purchase and reads where it stands; it never pays, approves, saves a card or logs in at a store. A verified person presses Pagar on the dashboard and completes what the purchase waits for (the protected card or CVV form, the store login); the purchase then runs through the same executor as the Shop's own chat, at most once at the store. No answer carries a card number, a security code, a cookie or a collect secret. Test projects only in this first delivery; behind SHOP_PURCHASES_ENABLED.

Query parameters

NameTypeRequiredDescription
ownerstringnoDeprecated and ignored (Shop v0.3): the list is always the authenticated person's own; it no longer selects another person's cards.

Responses

StatusBodyDescription
200objectOK
403objectThe bearer carries no verified person, or the key does not hold shop:instruments:read.

Response 200

FieldTypeRequiredDescription
instrumentsarray of objectyes—
Example request
curl -X GET https://api.codespar.dev/v1/shop/agent/instruments \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
GET /v1/shop/agent/instruments HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
import os
import requests

res = requests.get(
    "https://api.codespar.dev/v1/shop/agent/instruments",
    headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()
const res = await fetch("https://api.codespar.dev/v1/shop/agent/instruments", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
  },
});

const data = await res.json();
const r = await cs.api.response("get", "/v1/shop/agent/instruments");
// r.status is one of the documented statuses (200, 403),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
  console.log(r.data);
}
Example response 200
application/json
{
  "instruments": [
    {
      "id": "obj_0000000000000000",
      "kind": "byo_card",
      "brand": "string",
      "last4": "string",
      "status": "active"
    }
  ]
}

POST /v1/shop/agent/purchases

POSThttps://api.codespar.dev/v1/shop/agent/purchases

Propose a purchase at a supported store (never an order)

Scope shop:purchases:write. The store, its origin and its prices come from the server's registry and the store's own catalog, never from the request. The same agent_id and idempotency_key with the same intent answers the same purchase (idempotent_replay: true). Instead of merchant and items, offer_id (with an optional quantity) names an offer of this key's research: the store and the SKU come from it and the store quotes it again; a moved price or shipping is offer_stale (409) and an offer that is a comparison is offer_not_purchasable (409), with nothing proposed.

What an assistant can and cannot do. It proposes a purchase and reads where it stands; it never pays, approves, saves a card or logs in at a store. A verified person presses Pagar on the dashboard and completes what the purchase waits for (the protected card or CVV form, the store login); the purchase then runs through the same executor as the Shop's own chat, at most once at the store. No answer carries a card number, a security code, a cookie or a collect secret. Test projects only in this first delivery; behind SHOP_PURCHASES_ENABLED.

Request body

FieldTypeRequiredDescription
agent_idstringyes—
idempotency_keystringyes—
itemsarray of objectnoThe cart by SKU. Required with merchant; absent with offer_id.
merchantstringnoThe store id. Required with items; absent with offer_id.
offer_idstringnoShop v0.3: an offer of this key's research (POST /v1/shop/agent/research). The store and the SKU come from it; the store quotes it again.
payment_method"card" | "store_on_file"no—
quantityintegernoWith offer_id: how many units (default 1).

Responses

StatusBodyDescription
200objectOK
201objectOK
400objectThe body did not match the schema.
403objectThe key does not hold shop:purchases:write.
404objectThe offer is not in this key's research.
409objectNo mandate permits the store, the project is not Test, or the offer moved or cannot be bought here.
422objectThe agent, the store, the rail or an item is not one that can be bought.
503objectShop purchases are off on this deployment.

Response 200

FieldTypeRequiredDescription
idempotent_replaybooleanyes—
proposalobject,nullyesThe proposal card as the dashboard shows it.
purchaseobjectyes—
Example request
curl -X POST https://api.codespar.dev/v1/shop/agent/purchases \
  -H "Authorization: Bearer $CODESPAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "agent_id": "agt_0000000000000000",
       "merchant": "string",
       "items": [
         {
           "sku_id": "sku_0000000000000000",
           "quantity": 0
         }
       ],
       "offer_id": "offer_0000000000000000",
       "quantity": 0,
       "payment_method": "card",
       "idempotency_key": "string"
     }'
POST /v1/shop/agent/purchases HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json

{
  "agent_id": "agt_0000000000000000",
  "merchant": "string",
  "items": [
    {
      "sku_id": "sku_0000000000000000",
      "quantity": 0
    }
  ],
  "offer_id": "offer_0000000000000000",
  "quantity": 0,
  "payment_method": "card",
  "idempotency_key": "string"
}
import os
import requests

res = requests.post(
    "https://api.codespar.dev/v1/shop/agent/purchases",
    headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
    json={
      "agent_id": "agt_0000000000000000",
      "merchant": "string",
      "items": [
        {
          "sku_id": "sku_0000000000000000",
          "quantity": 0
        }
      ],
      "offer_id": "offer_0000000000000000",
      "quantity": 0,
      "payment_method": "card",
      "idempotency_key": "string"
    },
)
res.raise_for_status()
data = res.json()
const res = await fetch("https://api.codespar.dev/v1/shop/agent/purchases", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "agent_id": "agt_0000000000000000",
    "merchant": "string",
    "items": [
      {
        "sku_id": "sku_0000000000000000",
        "quantity": 0
      }
    ],
    "offer_id": "offer_0000000000000000",
    "quantity": 0,
    "payment_method": "card",
    "idempotency_key": "string"
  }),
});

const data = await res.json();
const r = await cs.api.response("post", "/v1/shop/agent/purchases", {
  body: {
    agent_id: "agt_0000000000000000",
    merchant: "string",
    items: [
      {
        sku_id: "sku_0000000000000000",
        quantity: 0
      }
    ],
    offer_id: "offer_0000000000000000",
    quantity: 0,
    payment_method: "card",
    idempotency_key: "string"
  }
});
// r.status is one of the documented statuses (200, 403, 422),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
  console.log(r.data);
}
Example response 200
application/json
{
  "purchase": {
    "id": "obj_0000000000000000",
    "kind": "purchase",
    "state": "drafted",
    "run_state": "ready",
    "awaiting": {
      "reason": "instrument"
    },
    "merchant": {
      "id": "obj_0000000000000000",
      "name": "Example"
    },
    "items": [
      {
        "sku_id": "sku_0000000000000000",
        "name": "Example",
        "quantity": 0,
        "unit_price_minor": 1
      }
    ],
    "total_minor": 1,
    "currency": "BRL",
    "payment_method": "card",
    "order": {
      "id": "obj_0000000000000000",
      "status": "string",
      "amount_minor": 1000
    },
    "receipt": {
      "funding_origin": "external_card",
      "wallet_debit": false
    },
    "failure": {
      "code": "string"
    },
    "next_action": "string"
  },
  "proposal": {},
  "idempotent_replay": true
}

GET /v1/shop/agent/purchases/{id}

GEThttps://api.codespar.dev/v1/shop/agent/purchases/{id}

Where a purchase stands

Scope shop:purchases:read. References and states only: the order the store answered, a receipt marked as external (no Wallet debit), what it waits for.

What an assistant can and cannot do. It proposes a purchase and reads where it stands; it never pays, approves, saves a card or logs in at a store. A verified person presses Pagar on the dashboard and completes what the purchase waits for (the protected card or CVV form, the store login); the purchase then runs through the same executor as the Shop's own chat, at most once at the store. No answer carries a card number, a security code, a cookie or a collect secret. Test projects only in this first delivery; behind SHOP_PURCHASES_ENABLED.

Path parameters

NameTypeRequiredDescription
idstringyesThe purchase proposal id (prp_...), from POST /v1/shop/agent/purchases.

Responses

StatusBodyDescription
200objectOK
403objectThe key does not hold shop:purchases:read.
404objectNot Found

Response 200

FieldTypeRequiredDescription
purchaseobjectyes—
Example request
curl -X GET https://api.codespar.dev/v1/shop/agent/purchases/{id} \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
GET /v1/shop/agent/purchases/{id} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
import os
import requests

res = requests.get(
    "https://api.codespar.dev/v1/shop/agent/purchases/{id}",
    headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()
const res = await fetch("https://api.codespar.dev/v1/shop/agent/purchases/{id}", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
  },
});

const data = await res.json();
const r = await cs.api.response("get", "/v1/shop/agent/purchases/{id}", {
  path: {
    id: "purchase_0000000000000000"
  }
});
// r.status is one of the documented statuses (200, 403),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
  console.log(r.data);
}
Example response 200
application/json
{
  "purchase": {
    "id": "obj_0000000000000000",
    "kind": "purchase",
    "state": "drafted",
    "run_state": "ready",
    "awaiting": {
      "reason": "instrument"
    },
    "merchant": {
      "id": "obj_0000000000000000",
      "name": "Example"
    },
    "items": [
      {
        "sku_id": "sku_0000000000000000",
        "name": "Example",
        "quantity": 0,
        "unit_price_minor": 1
      }
    ],
    "total_minor": 1,
    "currency": "BRL",
    "payment_method": "card",
    "order": {
      "id": "obj_0000000000000000",
      "status": "string",
      "amount_minor": 1000
    },
    "receipt": {
      "funding_origin": "external_card",
      "wallet_debit": false
    },
    "failure": {
      "code": "string"
    },
    "next_action": "string"
  }
}

POST /v1/shop/agent/research

POSThttps://api.codespar.dev/v1/shop/agent/research

Research a pedido aberto: offers compared, one recommendation

Scope shop:purchases:write (a research proposes nothing). The server consults the sources (registry, the stores the Shop can buy at; vtex and meli, comparison only; default registry in Test, vtex and meli in Live), records every offer with its source and observed_at, computes each total (price plus shipping), and recommends one principal with reason codes and at most three alternatives. lowest_price states the lowest total as "menor preço entre as ofertas consultadas", with the count and window it covers. decisive_unknowns lists the only questions that would change the principal. The research belongs to the project: a key carries no person, and never reaches a person's research. An offer older than 15 minutes is stale: a purchase from it is quoted again, as every purchase from an offer is.

What an assistant can and cannot do. It proposes a purchase and reads where it stands; it never pays, approves, saves a card or logs in at a store. A verified person presses Pagar on the dashboard and completes what the purchase waits for (the protected card or CVV form, the store login); the purchase then runs through the same executor as the Shop's own chat, at most once at the store. No answer carries a card number, a security code, a cookie or a collect secret. Test projects only in this first delivery; behind SHOP_PURCHASES_ENABLED.

Request body

FieldTypeRequiredDescription
criteriaobjectno—
querystringyesThe pedido aberto, as the person said it (e.g. "fone bluetooth").

Responses

StatusBodyDescription
201objectOK
400objectThe body did not match the schema.
403objectThe key does not hold shop:purchases:write.
409objectNo source of the asked groups exists in this environment.
503objectNo source answered, or Shop purchases are off on this deployment.

Response 201

FieldTypeRequiredDescription
researchobjectyesThe research as CONTRATO-I2 describes it: offers, recommendation, sources.
Example request
curl -X POST https://api.codespar.dev/v1/shop/agent/research \
  -H "Authorization: Bearer $CODESPAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "query": "string",
       "criteria": {
         "priority": "total",
         "max_total_minor": 1,
         "sources": [
           "registry"
         ],
         "limit": 1000
       }
     }'
POST /v1/shop/agent/research HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json

{
  "query": "string",
  "criteria": {
    "priority": "total",
    "max_total_minor": 1,
    "sources": [
      "registry"
    ],
    "limit": 1000
  }
}
import os
import requests

res = requests.post(
    "https://api.codespar.dev/v1/shop/agent/research",
    headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
    json={
      "query": "string",
      "criteria": {
        "priority": "total",
        "max_total_minor": 1,
        "sources": [
          "registry"
        ],
        "limit": 1000
      }
    },
)
res.raise_for_status()
data = res.json()
const res = await fetch("https://api.codespar.dev/v1/shop/agent/research", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "query": "string",
    "criteria": {
      "priority": "total",
      "max_total_minor": 1,
      "sources": [
        "registry"
      ],
      "limit": 1000
    }
  }),
});

const data = await res.json();
const r = await cs.api.response("post", "/v1/shop/agent/research", {
  body: {
    query: "string",
    criteria: {
      priority: "total",
      max_total_minor: 1,
      sources: [
        "registry"
      ],
      limit: 1000
    }
  }
});
// r.status is one of the documented statuses (200, 403),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
  console.log(r.data);
}
Example response 201
application/json
{
  "research": {}
}

GET /v1/shop/agent/research/{id}

GEThttps://api.codespar.dev/v1/shop/agent/research/{id}

Read a research back

Scope shop:purchases:read. The research this project's keys ran, with each offer's stale computed at the time of the answer.

What an assistant can and cannot do. It proposes a purchase and reads where it stands; it never pays, approves, saves a card or logs in at a store. A verified person presses Pagar on the dashboard and completes what the purchase waits for (the protected card or CVV form, the store login); the purchase then runs through the same executor as the Shop's own chat, at most once at the store. No answer carries a card number, a security code, a cookie or a collect secret. Test projects only in this first delivery; behind SHOP_PURCHASES_ENABLED.

Path parameters

NameTypeRequiredDescription
idstringyesThe research id (srr_...), from POST /v1/shop/agent/research.

Responses

StatusBodyDescription
200objectOK
403objectThe key does not hold shop:purchases:read.
404objectNot Found

Response 200

FieldTypeRequiredDescription
researchobjectyesThe research as CONTRATO-I2 describes it: offers, recommendation, sources.
Example request
curl -X GET https://api.codespar.dev/v1/shop/agent/research/{id} \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
GET /v1/shop/agent/research/{id} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
import os
import requests

res = requests.get(
    "https://api.codespar.dev/v1/shop/agent/research/{id}",
    headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()
const res = await fetch("https://api.codespar.dev/v1/shop/agent/research/{id}", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
  },
});

const data = await res.json();
const r = await cs.api.response("get", "/v1/shop/agent/research/{id}", {
  path: {
    id: "research_0000000000000000"
  }
});
// r.status is one of the documented statuses (200, 403),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
  console.log(r.data);
}
Example response 200
application/json
{
  "research": {}
}
Shop | CodeSpar