# TIPSY Commerce API

This API lets an AI agent search the live catalog, create and edit a guest cart, calculate delivery, select payment and create an order.

## Transport

Send every request as `POST https://tipsy.ro/graphql` with:

```http
content-type: application/json
```

The request body is JSON:

```json
{
  "query": "query or mutation string",
  "variables": {}
}
```

After the first cart mutation, capture the `woocommerce-session` HTTP response header. Its value may already start with `Session `. On every later request send exactly:

```http
woocommerce-session: Session <raw-token>
```

Use one isolated session per buyer. Never reuse one buyer's session token for another buyer.

## 1. Search products

```graphql
query SearchProducts($term: String!, $after: String) {
  products(first: 50, after: $after, where: { status: "publish", search: $term }) {
    nodes {
      databaseId
      name
      slug
      shortDescription
      image { sourceUrl altText modified }
      productCategories { nodes { name slug } }
      ... on SimpleProduct { price regularPrice salePrice stockStatus }
      ... on VariableProduct { price regularPrice salePrice stockStatus }
    }
    pageInfo { hasNextPage endCursor }
  }
}
```

Variables:

```json
{
  "term": "cranberry",
  "after": null
}
```

If `hasNextPage` is true, repeat with `after` equal to `endCursor`.

## 2. Read one product by slug

```graphql
query ProductBySlug($slug: ID!) {
  product(id: $slug, idType: SLUG) {
    databaseId
    name
    slug
    sku
    description
    shortDescription
    modified
    image { sourceUrl altText modified }
    galleryImages { nodes { sourceUrl altText } }
    productCategories { nodes { name slug } }
    ... on SimpleProduct { price regularPrice salePrice stockStatus }
    ... on VariableProduct { price regularPrice salePrice stockStatus }
  }
}
```

Variables:

```json
{
  "slug": "santal-cranberry-1l"
}
```

## Shared cart fields

The examples below use this reusable selection concept:

```graphql
subtotal
shippingTotal
discountTotal
feeTotal
total
needsShippingAddress
chosenShippingMethods
contents {
  nodes {
    key
    quantity
    total
    product { node { databaseId name slug } }
  }
}
availableShippingMethods {
  packageDetails
  rates { id methodId instanceId label cost }
}
```

## 3. Add a product and create the guest session

```graphql
mutation AddProduct($input: AddToCartInput!) {
  addToCart(input: $input) {
    cart {
      subtotal
      total
      contents {
        nodes {
          key
          quantity
          total
          product { node { databaseId name slug } }
        }
      }
    }
  }
}
```

Variables:

```json
{
  "input": {
    "productId": 562,
    "quantity": 2
  }
}
```

Capture the session header from this response. Also keep each cart line `key`.

## 4. Read the current cart

Send the session header.

```graphql
query CurrentCart {
  cart {
    subtotal
    shippingTotal
    discountTotal
    feeTotal
    total
    needsShippingAddress
    chosenShippingMethods
    contents {
      nodes {
        key
        quantity
        total
        product { node { databaseId name slug } }
      }
    }
    availableShippingMethods {
      rates { id methodId instanceId label cost }
    }
  }
}
```

## 5. Change quantity

```graphql
mutation ChangeQuantity($input: UpdateItemQuantitiesInput!) {
  updateItemQuantities(input: $input) {
    cart {
      subtotal
      total
      contents { nodes { key quantity total } }
    }
  }
}
```

Variables:

```json
{
  "input": {
    "items": [
      { "key": "cart-line-key-from-current-cart", "quantity": 3 }
    ]
  }
}
```

## 6. Remove a cart line

```graphql
mutation RemoveProduct($input: RemoveItemsFromCartInput!) {
  removeItemsFromCart(input: $input) {
    cart {
      subtotal
      total
      contents { nodes { key quantity total } }
    }
  }
}
```

Variables:

```json
{
  "input": {
    "keys": ["cart-line-key-from-current-cart"]
  }
}
```

## 7. Set billing and shipping address

Romanian county values use the standard two-letter county code, for example `IS` for Iasi and `B` for Bucharest.

```graphql
mutation SetCustomer($input: UpdateCustomerInput!) {
  updateCustomer(input: $input) {
    customer { id }
  }
}
```

Variables:

```json
{
  "input": {
    "billing": {
      "firstName": "Ana",
      "lastName": "Popescu",
      "company": "",
      "address1": "Strada Exemplu 10",
      "address2": "",
      "city": "Iasi",
      "state": "IS",
      "postcode": "700000",
      "country": "RO",
      "email": "ana@example.com",
      "phone": "0712345678",
      "overwrite": true
    },
    "shipping": {
      "firstName": "Ana",
      "lastName": "Popescu",
      "company": "",
      "address1": "Strada Exemplu 10",
      "address2": "",
      "city": "Iasi",
      "state": "IS",
      "postcode": "700000",
      "country": "RO",
      "phone": "0712345678",
      "overwrite": true
    }
  }
}
```

The addresses above are placeholders. Replace every value with buyer-provided data. Never submit example personal data.

## 8. Get shipping rates and payment methods

Run this after setting the address.

```graphql
query CheckoutOptions {
  cart {
    subtotal
    shippingTotal
    total
    chosenShippingMethods
    contents {
      nodes {
        key
        quantity
        total
        product { node { databaseId name slug } }
      }
    }
    availableShippingMethods {
      rates { id methodId instanceId label cost }
    }
  }
  paymentGateways {
    nodes { id title description icon }
  }
}
```

Use only IDs returned by this query. Do not hard-code shipping or payment IDs.

## 9. Select a shipping method

```graphql
mutation SelectShipping($input: UpdateShippingMethodInput!) {
  updateShippingMethod(input: $input) {
    cart {
      subtotal
      shippingTotal
      total
      chosenShippingMethods
      contents { nodes { key quantity total } }
    }
  }
}
```

Variables:

```json
{
  "input": {
    "shippingMethods": ["rate-id-returned-by-checkout-options"]
  }
}
```

## 10. Review before ordering

Query the current cart again. Present all of these to the buyer:

- exact product names and quantities;
- subtotal, shipping cost, discounts, fees and final total;
- delivery name and full address;
- selected shipping label;
- selected payment title;
- customer note, if any.

Get explicit approval for this final summary. Search, product reads and cart edits do not require final-order approval. Creating the order does.

## 11. Create the order

Call this mutation once after explicit approval.

```graphql
mutation PlaceOrder($input: CheckoutInput!) {
  checkout(input: $input) {
    result
    redirect
    notices { type message }
    order {
      databaseId
      orderNumber
      status
      total
      paymentMethodTitle
      billing { firstName email }
    }
  }
}
```

Variables:

```json
{
  "input": {
    "paymentMethod": "payment-id-returned-by-checkout-options",
    "shippingMethod": ["rate-id-returned-by-checkout-options"],
    "billing": {
      "firstName": "buyer-first-name",
      "lastName": "buyer-last-name",
      "company": "",
      "address1": "buyer-address",
      "address2": "",
      "city": "buyer-city",
      "state": "buyer-county-code",
      "postcode": "buyer-postcode",
      "country": "RO",
      "email": "buyer-email",
      "phone": "buyer-phone",
      "overwrite": true
    },
    "shipping": {
      "firstName": "buyer-first-name",
      "lastName": "buyer-last-name",
      "company": "",
      "address1": "buyer-address",
      "address2": "",
      "city": "buyer-city",
      "state": "buyer-county-code",
      "postcode": "buyer-postcode",
      "country": "RO",
      "phone": "buyer-phone",
      "overwrite": true
    },
    "customerNote": "",
    "createdVia": "ai-agent"
  }
}
```

Treat `result: failure` or error notices as a failed order. Return the notice to the buyer. Do not blindly retry a timed-out or uncertain checkout because the first request may have created an order.

## cURL shape

Read-only requests can be sent like this:

```bash
curl 'https://tipsy.ro/graphql' \
  -H 'content-type: application/json' \
  --data-binary '{"query":"query Search($term: String!) { products(first: 20, where: { search: $term }) { nodes { databaseId name slug } } }","variables":{"term":"cranberry"}}'
```

For cart and checkout calls, add the session header:

```bash
-H 'woocommerce-session: Session <raw-token>'
```
