---
title: "SDK"
space: "Commera"
url: "https://docs.bwh.tech/commera/plugin-reference/sdk"
updated: "2026-10-09"
---

`commera.sdk` is the stable Python API for plugins. Use it instead of Commera's internal modules, which can change in any release. Every read checks the session user's permissions.

| Name | What it does |
| --- | --- |
| `commera.sdk.API_VERSION` | The API version this Commera supports, `1` |
| `commera.sdk.STORE_ORDER_TYPE` | `"Shopping Cart"`, the **Order Type** of a store order |
| `commera.sdk.as_plugin_user(app)` | Runs a block as Commera's plugin user |
| `commera.sdk.orders.get_order` | Reads one order |
| `commera.sdk.orders.get_orders` | Reads many orders |
| `commera.sdk.orders.record_shipment` | Records a shipment for an order |
| `commera.sdk.catalog.get_items` | Reads items with their store price and stock |
| `commera.sdk.cart.get_cart` | Reads the session shopper's cart |
| `commera.sdk.cart.set_cart_fields` | Saves your fields on the cart |
| `commera.sdk.events.CommeraEvent` | The argument of an event handler. See [Events](/commera/plugin-reference/events) |

The return shapes are `TypedDict`s in `commera.sdk.types`.

## get_order

```python
from commera.sdk import orders

order = orders.get_order("SAL-ORD-2026-00062", extra_fields=["my_app_status"])
```

| Parameter | Type | Value |
| --- | --- | --- |
| `sales_order` | `str \| int` | The Sales Order name |
| `extra_fields` | `list[str]` | Your own Sales Order fields to read into `plugin_fields`. Each must start with your app name |

Returns an `Order` dict:

| Key | Value |
| --- | --- |
| `name`, `customer`, `customer_name`, `email`, `phone` | The order and its customer |
| `shipping_address` | An `Address` dict: `name`, `address_title`, `address_line1`, `address_line2`, `city`, `state`, `pincode`, `country`, `phone`. The shipping address, else the billing address. `None` when there is neither |
| `placed_on` | The transaction date |
| `order_type` | The **Order Type** |
| `currency`, `total`, `net_total`, `grand_total`, `rounded_total` | Totals in the order currency |
| `base_grand_total`, `base_rounded_total` | Totals in the company currency |
| `payment_mode` | The checkout payment mode, for example `COD` |
| `is_paid`, `is_cancelled` | Booleans |
| `status` | The order status that events fire on, for example `Shipped` |
| `stage` | `{ key, label }`, the badge that the dashboard shows |
| `plugin_fees` | A list of `{ description, amount }`, the fees that plugins added |
| `items` | A list of lines: `line_id` (the Sales Order Item name), `item_code`, `title`, `size`, `qty`, `delivered_qty`, `rate`, `amount`, `image` |
| `tags` | The order's tags |
| `plugin_fields` | The values of `extra_fields` |

**Throws** `frappe.PermissionError` when the user cannot read the order, `frappe.DoesNotExistError` when it does not exist, and `frappe.ValidationError` for a field that your plugin does not own.

## get_orders

```python
found = orders.get_orders(["SAL-ORD-2026-00062", "SAL-ORD-2026-00063"])
```

Returns a dict of `Order` keyed by the order name as a string. An order that the user cannot read, or that does not exist, is left out.

## record_shipment

Records a shipment for an order: Commera creates or updates the order's Shipping Request for the AWB. It makes no Delivery Note. The order status, the `order_fulfilled` and `order_delivered` events, and the shopper's tracking page follow from it.

```python
from commera.sdk import orders

orders.record_shipment(
	"SAL-ORD-2026-00062",
	awb="1234567890",
	carrier="Delhivery",
	tracking_url="https://example.com/track/1234567890",
	status="In Transit",
)
```

| Parameter | Type | Default | Value |
| --- | --- | --- | --- |
| `sales_order` | `str \| int` | | The Sales Order name |
| `awb` | `str` | | Required, keyword only. The tracking number. Commera finds the Shipping Request by order and AWB |
| `provider` | `str \| None` | `None` | A **Shipping Provider Profile** name. Leave it empty when a fulfilment partner made the shipment |
| `carrier` | `str \| None` | `None` | The carrier name the shopper sees |
| `status` | `str` | `"In Transit"` | One of Draft, Ready To Ship, Pickup Scheduled, In Transit, Out For Delivery, Undelivered, RTO, Delivered, Cancelled, Lost |
| `events` | `list[dict] \| None` | `None` | Tracking events to add to the shipment |
| `tracking_url` | `str \| None` | `None` | A link the shopper can open to track the parcel |

Returns the Shipping Request name.

- A status only moves forward. A lower status than the stored one is ignored. Delivered, Cancelled and Lost are final.
- Call it again with the same AWB to update the status, the tracking events and the `tracking_url`. Commera does not make a second shipment. A `carrier` on a later call is ignored: the first call sets it.
- Without `provider`, the shipment has no origin address, because the parcel left the partner's warehouse.

**Throws** `ShippingNotInstalled` (from `commera.sdk.orders`) when `bwh_shipping` is not installed, `frappe.ValidationError` for an empty AWB, an unknown status or an unknown provider, and `frappe.PermissionError` when the user cannot read the order.

:::note[Partner shipments need a recent bwh_shipping]

A call without `provider` needs a `bwh_shipping` version in which the Shipping Request provider is optional and has a tracking URL. With an older version, pass a `provider`.

:::

## get_items

```python
from commera.sdk import catalog

items = catalog.get_items(["TSHIRT-RED-M", "TSHIRT-RED-L"])
```

Returns a dict of `CatalogItem` keyed by item code. A code that is not an Item is left out.

| Key | Value |
| --- | --- |
| `item_code`, `title` | The Item |
| `product` | The template Item, or the item itself when it has no template |
| `is_listed` | `True` when a published option on the storefront sells the item |
| `price` | What a shopper pays. `None` until the store's price lists are set in **Commera Settings** |
| `list_price` | The struck-through price, or `None` |
| `currency` | The store currency |
| `available_qty` | The quantity the store warehouse can sell. For an item that sells without stock, it can be 0 or less. See [Gotchas](/commera/plugin-reference/gotchas) |

**Throws** `frappe.PermissionError` when the user cannot read Item.

## get_cart and set_cart_fields

`get_cart()` returns the session shopper's cart, a guest's included, or `None` when they have no cart. The `Cart` dict has `name`, `customer`, `currency`, `items` (`item_code`, `title`, `qty`, `rate`, `amount`), `total`, `grand_total` and `plugin_fields` (every Quotation field that an installed Commera plugin owns).

`set_cart_fields(values)` saves your own Quotation fields on the cart, prices your fees again, and returns the checkout summary: `subtotal`, `shipping`, `cod_charge`, `plugin_fees`, `taxes`, `discount_amount`, `rounding_adjustment`, `total`, and the same figures for cash on delivery under `cash_on_delivery`.

```python
import frappe
from commera.sdk import cart


@frappe.whitelist()
def set_gift_wrap(enabled: bool):
	return cart.set_cart_fields({"my_app_gift_wrap": int(enabled)})
```

`set_cart_fields` throws `frappe.DoesNotExistError` when the shopper has no cart, and `frappe.ValidationError` for a field that your plugin does not own, or when the cart is already paid or a payment for it is in progress.

## as_plugin_user

A webhook from a partner arrives as Guest, with no permission to read orders. Run the SDK calls inside `as_plugin_user`:

```python
import frappe
from commera.sdk import as_plugin_user, orders


@frappe.whitelist(allow_guest=True, methods=["POST"])
def shipment_webhook():
	payload = frappe.parse_json(frappe.request.get_data(as_text=True))
	# verify the partner's signature here before you trust the payload
	with as_plugin_user("my_app"):
		orders.record_shipment(payload["order"], awb=payload["awb"], tracking_url=payload.get("tracking_url"))
```

The plugin user holds every desk role. Verify the request before you enter the block. `as_plugin_user` throws when the plugin is not installed.

## Next steps

- [Events](/commera/plugin-reference/events)
- [Compatibility](/commera/plugin-reference/compatibility)
- [Testing](/commera/plugin-reference/testing)
