Skip to main content

Modern async-first Python SDK for the Unicommerce REST API

Project description

unicommerce

A modern, async-first Python SDK for the Unicommerce REST API.

  • Python 3.12+
  • Sync and async clients (powered by httpx)
  • Pydantic v2 models for all responses
  • OAuth 2.0 with automatic token refresh
  • Retry with exponential backoff
  • Custom exception hierarchy
  • 100 API methods across 9 resource domains

Installation

pip install unicommerce

Quick Start

from unicommerce import Unicommerce

client = Unicommerce(
    tenant="your-tenant",
    username="your-username",
    password="your-password",
)

# Search sale orders
results = client.sale_orders.search(status="CREATED")
for order in results.sale_orders:
    print(order.code, order.status_code)

# Get full order details
order = client.sale_orders.get(code="SO-12345")
print(order.sale_order_code)

# Adjust inventory
client.inventory.adjust(
    item_sku="SKU-100",
    quantity=10,
    shelf_code="DEFAULT",
    adjustment_type="ADD",
)

client.close()

Async Usage

import asyncio
from unicommerce import AsyncUnicommerce

async def main():
    async with AsyncUnicommerce(
        tenant="your-tenant",
        username="your-username",
        password="your-password",
    ) as client:
        order = await client.sale_orders.get(code="SO-12345")
        print(order.sale_order_code)

asyncio.run(main())

API Reference

Sale Orders (18 methods)

# Search orders with filters
results = client.sale_orders.search(status="CREATED", channel="MYNTRA")

# Get full order with items and shipping packages
order = client.sale_orders.get(code="SO-12345")

# Create a sale order
from unicommerce.models import CreateSaleOrderRequest
order = client.sale_orders.create(CreateSaleOrderRequest(...))

# Cancel (full or partial)
client.sale_orders.cancel(
    sale_order_code="SO-12345",
    sale_order_item_codes=["SOI-001"],
    cancellation_reason="Customer request",
    cancel_partially=True,
)

# Edit addresses
client.sale_orders.edit(
    sale_order_code="SO-12345",
    addresses=[{"type": "SHIPPING", "city": "Mumbai"}],
)

# Verify / set priority
client.sale_orders.verify(sale_order_code="SO-12345")
client.sale_orders.set_priority(sale_order_code="SO-12345", priority=1)

# Customer management
client.sale_orders.create_customer(customer={"name": "John", "email": "john@example.com"})
client.sale_orders.edit_customer(customer={"code": "C-001", "name": "John Doe"})

# Edit order/item metadata
client.sale_orders.edit_metadata(
    sale_order_code="SO-12345",
    priority=1,
    custom_field_values=[{"name": "field1", "value": "val1"}],
)
client.sale_orders.edit_item_metadata(
    sale_order_code="SO-12345",
    sale_order_item_code="SOI-001",
    custom_field_values=[{"name": "field1", "value": "val1"}],
)

# Hold / unhold orders and items
client.sale_orders.hold(sale_order_code="SO-12345")
client.sale_orders.unhold(sale_order_code="SO-12345")
client.sale_orders.hold_items(sale_order_code="SO-12345", sale_order_item_codes=["SOI-001"])
client.sale_orders.unhold_items(sale_order_code="SO-12345", sale_order_item_codes=["SOI-001"])

# Switch facility
client.sale_orders.switch_facility(
    facility_code="WAREHOUSE-2",
    sale_order_code="SO-12345",
    sale_order_item_codes=["SOI-001"],
)

# Add item details (e.g. serial numbers, IMEI)
client.sale_orders.add_item_detail(
    sale_order_code="SO-12345",
    sale_order_item_code="SOI-001",
    item_details=[{"imeiNumber": "123456789"}],
)
client.sale_orders.add_item_detail_bulk(
    sale_order_code="SO-12345",
    sale_order_item_detail_dtos=[...],
)

Inventory (5 methods)

# Get inventory snapshot
snapshot = client.inventory.get_snapshot(updated_since_in_minutes=60)
for item in snapshot.inventory_snapshots:
    print(item.item_type_sku, item.inventory)

# Adjust inventory
client.inventory.adjust(
    item_sku="SKU-100",
    quantity=5,
    shelf_code="DEFAULT",
    adjustment_type="ADD",
    inventory_type="GOOD_INVENTORY",
)

# Bulk adjust
client.inventory.adjust_bulk(adjustments=[...])

# Mark quantity found (cycle count)
client.inventory.mark_found(
    item_sku="SKU-100",
    shelf_code="DEFAULT",
    quantity_found=10,
)

# Get nearby store inventory
client.inventory.get_nearby_store_inventory(
    channel_product_id="CP-001",
    pincode="110001",
)

Products (7 methods)

# Get product by SKU
product = client.products.get(sku_code="SKU-100")
print(product.name, product.category_name)

# Get product by item code
product = client.products.get_by_item_code(item_code="ITEM-001")

# Search products
results = client.products.search(skuCode="SKU-100")

# Create or edit a product
client.products.create_or_edit(item_type={"skuCode": "SKU-100", "name": "T-Shirt"})

# Bulk create or edit
client.products.create_or_edit_bulk(item_types=[...])

# Create or edit a category
client.products.create_or_edit_category(category={"name": "Apparel"})

# Create channel item type mapping
client.products.create_channel_item_type(
    channel_code="MYNTRA",
    channel_product_id="MYN-001",
    item_type_sku="SKU-100",
)

Fulfillment (32 methods)

# --- Shipping Packages ---

# Get shipping package details
sp = client.fulfillment.get_shipping_package(shipping_package_code="SP-001")

# Search shipping packages
results = client.fulfillment.search_shipping_packages(status="DISPATCHED")

# Create shipping package
client.fulfillment.create_shipping_package(
    sale_order_code="SO-12345",
    sale_order_item_codes=["SOI-001", "SOI-002"],
)

# Edit / split / modify shipping packages
client.fulfillment.edit_shipping_package(shipping_package_code="SP-001", actualWeight=1.5)
client.fulfillment.split_shipping_package(
    shipping_package_code="SP-001",
    split_packages=[{"saleOrderItemCodes": ["SOI-001"]}],
)
client.fulfillment.modify_shipping_package(
    sale_order_code="SO-12345",
    sale_order_item_codes=["SOI-001"],
)

# Get shipping packages by status
client.fulfillment.get_shipping_packages(status_code="DISPATCHED")

# --- Invoicing ---

# Create invoice
client.fulfillment.create_invoice(shipping_package_code="SP-001")

# Get invoice details
invoice = client.fulfillment.get_invoice_details(shipping_package_code="SP-001")

# Get invoice label (base64 PDF)
label = client.fulfillment.get_invoice_label(shipping_package_code="SP-001")

# Create invoice + shipping label
result = client.fulfillment.create_invoice_and_label(
    shipping_package_code="SP-001",
    generate_uniware_shipping_label=True,
)

# Create invoice by sale order code
client.fulfillment.create_invoice_by_sale_order(
    sale_order_code="SO-12345",
    sale_order_item_codes=["SOI-001"],
)

# Create invoice with full details
client.fulfillment.create_invoice_with_details(
    sale_order_code="SO-12345",
    invoice={"invoiceDate": "2026-06-21"},
)

# Download invoice as PDF
pdf = client.fulfillment.get_invoice_pdf(invoice_codes=["INV-001"])
pdf.save("invoice.pdf")         # save to file
raw_bytes = pdf.content          # or access raw bytes

# Download shipping label as PDF
label_pdf = client.fulfillment.get_shipping_label_pdf(shipping_package_codes=["SP-001"])
label_pdf.save("label.pdf")

# Create invoice and allocate shipping provider
client.fulfillment.create_invoice_and_allocate_provider(shipping_package_code="SP-001")

# --- Shipping Providers ---

# Allocate shipping provider
client.fulfillment.allocate_shipping_provider(shipping_package_code="SP-001")

# Check pincode serviceability
client.fulfillment.check_serviceability(pincode="110001", cash_on_delivery=False)

# --- Manifests ---

# Create manifest
manifest = client.fulfillment.create_manifest(
    channel="MYNTRA",
    third_party_shipping=True,
    shipping_provider_code="DELHIVERY",
)

# Get manifest
manifest = client.fulfillment.get_manifest(shipping_manifest_code="SM-001")

# Add packages to manifest
client.fulfillment.add_package_to_manifest(
    shipping_manifest_code="SM-001",
    shipping_package_codes=["SP-001", "SP-002"],
)

# Create and close manifest
client.fulfillment.create_and_close_manifest(channel="MYNTRA", thirdPartyShipping=True)

# Close manifest
client.fulfillment.close_manifest(shipping_manifest_code="SM-001")

# --- Dispatch ---

# Dispatch a shipping package
client.fulfillment.dispatch(shipping_package_code="SP-001")

# Force dispatch
client.fulfillment.force_dispatch(shipping_package_code="SP-001")

# Create and dispatch in one call
client.fulfillment.create_and_dispatch(
    sale_order_code="SO-12345",
    sale_order_item_info=[{"saleOrderItemCode": "SOI-001"}],
    shipping_package_info={"length": 10, "width": 10, "height": 10},
)

# Mark delivered
client.fulfillment.mark_delivered(
    sale_order_code="SO-12345",
    sale_order_item_codes=["SOI-001"],
)

# --- Other ---

# Update tracking status
client.fulfillment.update_tracking_status(
    provider_code="DELHIVERY",
    tracking_number="AWB123456",
    tracking_status="DELIVERED",
)

# Create picklist
client.fulfillment.create_picklist(shipping_package_codes=["SP-001", "SP-002"])

# Create or update reason list
client.fulfillment.create_or_update_reason(name="CANCEL_REASONS", value="Out of stock")

# Update seal ID
client.fulfillment.update_seal_id(
    shipping_package_code="SP-001",
    shipping_package_type_code="TYPE-1",
    spt_item_seal_id="SEAL-001",
)

# Bulk update seal IDs
client.fulfillment.update_seal_id_bulk(packages=[...])

Inbound (14 methods)

# --- Vendors ---

# Create vendor
client.inbound.create_vendor(vendor={"code": "V-001", "name": "Acme Corp"})

# Create or edit vendor catalog
client.inbound.create_or_edit_vendor_catalog(vendor_item_type={"vendorCode": "V-001", "skuCode": "SKU-100"})

# Get vendor backorder items
client.inbound.get_vendor_backorder_items(vendorCode="V-001")

# --- Purchase Orders ---

# Create purchase order
client.inbound.create_purchase_order(vendor_code="V-001")

# Get purchase order details
po = client.inbound.get_purchase_order(purchase_order_code="PO-001")

# Search purchase orders
results = client.inbound.search_purchase_orders(vendorCode="V-001")

# Approve purchase order
client.inbound.approve_purchase_order(purchase_order_code="PO-001")

# Create and approve in one call
client.inbound.create_and_approve_purchase_order(
    vendor_code="V-001",
    user_id="user@example.com",
    purchase_order_items=[{"itemSKU": "SKU-100", "quantity": 50}],
)

# Close purchase order
client.inbound.close_purchase_order(purchase_order_code="PO-001")

# --- GRN (Goods Receipt Notes) ---

# Create GRN
client.inbound.create_grn(purchase_order_code="PO-001")

# Get GRN details
grn = client.inbound.get_grn(inflow_receipt_code="IR-001")

# Search GRNs
results = client.inbound.search_grns(purchaseOrderCode="PO-001")

# Add item to GRN by SKU
client.inbound.add_item_to_grn(inflow_receipt_code="IR-001", item_sku="SKU-100", quantity=10)

# Add item to GRN by item code
client.inbound.add_item_to_grn_by_code(inflow_receipt_code="IR-001", item_code="ITEM-001")

Returns (11 methods)

# Search returns
results = client.returns.search(
    return_type="RTO",
    updated_from="2026-06-01T00:00:00.000Z",
    updated_to="2026-06-21T23:59:59.000Z",
)

# Get return details
ret = client.returns.get(shipment_code="SH-001")
ret = client.returns.get(reverse_pickup_code="RP-001")

# Create reverse pickup
client.returns.create_reverse_pickup(
    sale_order_code="SO-12345",
    sale_order_items=[{"code": "SOI-001", "reason": "DAMAGED"}],
)

# Edit reverse pickup
client.returns.edit_reverse_pickup(reverse_pickup_code="RP-001", courierCode="DELHIVERY")

# Approve / cancel reverse pickups
client.returns.approve_reverse_pickup(reverse_pickup_codes=["RP-001", "RP-002"])
client.returns.cancel_reverse_pickup(reverse_pickup_code="RP-001")

# Allocate courier for reverse pickup
client.returns.allocate_courier_for_reverse_pickup(reverse_pickup_codes=["RP-001"])

# Mark items as returned
client.returns.mark_returned(
    sale_order_code="SO-12345",
    sale_order_items=[{"code": "SOI-001"}],
    return_reason="DAMAGED",
)

# Mark returned with inventory type
client.returns.mark_returned_with_inventory_type(
    sale_order_code="SO-12345",
    sale_order_items=[{"code": "SOI-001", "inventoryType": "GOOD_INVENTORY"}],
)

# Alternate item handling
client.returns.create_alternate_item(
    sale_order_items=[{"code": "SOI-001"}],
    sale_order_item_alternates=[{"skuCode": "SKU-ALT"}],
)
client.returns.accept_alternate_item(
    sale_order_code="SO-12345",
    sale_order_item_codes=["SOI-001"],
    selected_alternate_item_sku="SKU-ALT",
)

Outbound / Gatepass (9 methods)

# Create gatepass
gp = client.outbound.create_gatepass(type="OUTBOUND", party_code="PARTY-001")

# Edit gatepass
client.outbound.edit_gatepass(gate_pass_code="GP-001", ws_gate_pass={...})

# Scan item into gatepass
client.outbound.scan_item(gate_pass_code="GP-001", item_code="ITEM-001")

# Add non-traceable item
client.outbound.add_nontraceable_item(
    gate_pass_code="GP-001",
    item_sku="SKU-100",
    inventory_type="GOOD_INVENTORY",
    quantity=5,
    unit_price=100.0,
    shelf_code="DEFAULT",
)

# Remove item from gatepass
client.outbound.remove_gatepass_item(gate_pass_code="GP-001", item_code="ITEM-001")

# Complete / discard gatepass
client.outbound.complete_gatepass(gate_pass_code="GP-001")
client.outbound.discard_gatepass(gate_pass_code="GP-001")

# Search gatepasses
results = client.outbound.search_gatepass(from_date="2026-06-01", to_date="2026-06-21")

# Get gatepass details
gp = client.outbound.get_gatepass(gate_pass_codes=["GP-001"])

Facilities (2 methods)

# Search facilities
results = client.facilities.search(
    from_date="2026-06-01T00:00:00.000Z",
    to_date="2026-06-21T23:59:59.000Z",
    date_type="CREATED",
)

# Get facility details
facility = client.facilities.get_details(facility_code="WAREHOUSE-1")
print(facility.name, facility.gst_number)

Export Jobs (2 methods)

# Create a bulk export job
job = client.export_jobs.create(
    export_job_type_name="Inventory Snapshot",
    export_columns=["inventory"],
    frequency="ONETIME",
)
print(job.job_code)

# Check export job status
status = client.export_jobs.get_status(job_code=job.job_code)
print(status.status, status.file_path)  # COMPLETE, https://...csv

Error Handling

from unicommerce.exceptions import (
    UnicommerceError,    # base for all errors
    AuthenticationError, # 401 - bad credentials
    AuthorizationError,  # 403 - insufficient permissions
    ValidationError,     # request validation failed
    RateLimitError,      # 429 - too many requests
    ApiError,            # API returned successful=false
    ServerError,         # 5xx
    NetworkError,        # connection failed
    TimeoutError,        # request timed out
)

try:
    order = client.sale_orders.get(code="SO-INVALID")
except AuthenticationError:
    print("Bad credentials")
except ApiError as e:
    print(f"API error: {e.errors}")
except UnicommerceError as e:
    print(f"Something went wrong: {e}")

Raw Response Access

Every response model exposes the raw API response via .raw:

order = client.sale_orders.get(code="SO-12345")
raw = order.raw  # dict with full unprocessed API response
print(raw["saleOrderDTO"]["channel"])

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

unicommerce-0.1.0.tar.gz (26.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

unicommerce-0.1.0-py3-none-any.whl (35.8 kB view details)

Uploaded Python 3

File details

Details for the file unicommerce-0.1.0.tar.gz.

File metadata

  • Download URL: unicommerce-0.1.0.tar.gz
  • Upload date:
  • Size: 26.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for unicommerce-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8b49bf48e0536cc7224a8c60c049476628cf056add04ddb5140a7428acde9f05
MD5 42f95ec67a036e255d130e28b25daf2e
BLAKE2b-256 fbbedca0b19e76a4e030c438af07ff92cec65501ab95ff65edb7b4a5d3dbb538

See more details on using hashes here.

File details

Details for the file unicommerce-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: unicommerce-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 35.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for unicommerce-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f3bf81141af7a14f4654d6ffad8c04e7df9a0465bec1e7b970bdb867285bf07c
MD5 469aa227567d18dfac6e0f6bf2231af1
BLAKE2b-256 4ab1d0de8e868352969c8b05f7f84764ec1c85b60aeeb1daa1b31808683f5384

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page