﻿# Agility API

Programmatic order intake for a single store. External systems send orders with
a store-scoped API key (`agl_live_…`).

Public documentation URL:

```text
https://www.agility.bg/docs
```

Downloadable Markdown:

```text
https://www.agility.bg/agility-api-docs.md
```

## Endpoints

| Action | Method | Path | Auth |
| --- | --- | --- | --- |
| **Import directly into Agility** | `POST` | `/api/v1/orders/import` | API key |
| Accept into our system (Google Sheets) | `POST` | `/api/v1/orders/system` | API key |

Base URL:

```text
https://www.agility.bg/api/v1
```

### Direct import (recommended for external software)

```text
POST https://www.agility.bg/api/v1/orders/import
```

Runs the full import pipeline (validation, address/office matching) and saves
orders into the store dashboard immediately.

### Into our system (Sheets buffer)

```text
POST https://www.agility.bg/api/v1/orders/system
```

Appends rows to the store’s Google Sheets tab. The merchant then syncs/imports
from the dashboard when ready.

## Authentication

Send the API key in the `Authorization` header:

```http
Authorization: Bearer agl_live_YOUR_KEY
```

Create the key from the dashboard:

```text
Settings → Order sync
```

## Request (direct import)

```bash
curl -s -X POST \
  -H "Authorization: Bearer agl_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orders": [
      {
        "Name": "#1024",
        "Created at": "2026-05-18T09:36:39.285Z",
        "Shipping Name": "Galina Gancheva",
        "Email": "",
        "Shipping Phone": "0894651847",
        "Total": 19.49,
        "Shipping": 0,
        "Shipping Method": "Speedy to_address",
        "Lineitem quantity": 1,
        "Lineitem name": "Product A",
        "Lineitem price": 19.49,
        "Lineitem sku": "",
        "Shipping Address1": "Ivan Shishman 4, floor 2",
        "Shipping City": "Ruse",
        "Shipping Zip": "7000",
        "Billing Country": "BG",
        "Courier": "Speedy",
        "Financial Status": "pending",
        "Payment Method": "Cash on Delivery (COD)",
        "Note Attributes": ""
      }
    ]
  }' \
  https://www.agility.bg/api/v1/orders/import
```

Maximum: 500 orders per request.

The same JSON body works for `/orders/system` — only change the URL.

## Payload fields

The request body must be a JSON object with an `orders` array. Each order can
contain Shopify-compatible field names below. Field names are case-sensitive.
For backward compatibility, the API also accepts snake_case aliases
(`order_code`, `customer_phone`, `address_raw`, …).

| Field | Required | Type | Used by our system | Description |
| --- | --- | --- | --- | --- |
| `Name` | Yes | string | Yes | Order number, for example `#1024`. Used as the unique order code. Rows with the same `Name` are grouped as one order with multiple line items. |
| `Email` | No | string | Yes | Customer email address. Stored on the order when available. |
| `Financial Status` | No | string | Yes | Payment state. `paid` marks the order as prepaid. `pending`/`unpaid` are treated as unpaid unless `Payment Method` indicates COD. |
| `Paid at` | No | string | Compatibility | Shopify-compatible timestamp for payment capture. Kept for schema compatibility; the importer primarily uses `Financial Status` and `Payment Method`. |
| `Fulfillment Status` | No | string | Compatibility | Shopify-compatible fulfillment status. Import status is controlled by our system, so this can be empty. |
| `Fulfilled at` | No | string | Compatibility | Shopify-compatible fulfillment timestamp. Usually empty for new imports. |
| `Accepts Marketing` | No | boolean/string | Compatibility | Shopify customer marketing flag. Not used for courier/import logic. |
| `Currency` | No | string | Compatibility | Currency code, for example `EUR` or `BGN`. Kept for Shopify-compatible totals context. |
| `Subtotal` | No | number | Yes | Product subtotal before shipping/taxes. If `Total` is missing, our system can fall back to `Subtotal + Shipping + Taxes`. Also used to detect free shipping cases. |
| `Shipping` | No | number | Yes | Shipping amount charged to the customer. Used in totals and COD/shipping reconciliation. Send `0` if no separate shipping price exists. |
| `Taxes` | No | number | Yes | Tax amount. Used only as part of the fallback total calculation when `Total` is missing. |
| `Total` | Yes | number | Yes | Final order total. Used as the primary order amount and, for COD orders, the COD base amount. |
| `Discount Code` | No | string | Yes | Discount code applied to the order. Stored and shown with the order when available. |
| `Discount Amount` | No | number | Yes | Discount amount. Stored with the order when available. |
| `Shipping Method` | Yes | string | Yes | Delivery method text. Include method and destination, for example `Speedy to_address`, `Speedy to_office`, `Econt to_address`, `Econt to_office`. The importer uses this to infer `to_address`/`to_office` when possible. |
| `Created at` | No | string | Yes | Order creation date/time. Use ISO 8601. If omitted, our system uses the intake time. |
| `Lineitem quantity` | No | number | Yes | Quantity for this line item. Multiple rows with the same `Name` become multiple order items. |
| `Lineitem name` | Yes | string | Yes | Product name for this line item. Used in product display and shipment notes. |
| `Lineitem price` | No | number | Yes | Product unit price. Stored on the line item and used in item details. |
| `Lineitem compare at price` | No | number | Fallback | Optional compare-at price. Used as a fallback item price when `Lineitem price` is missing. |
| `Lineitem sku` | No | string | Yes | Product SKU. Stored on the line item when available. |
| `Lineitem requires shipping` | No | boolean/string | Compatibility | Shopify-compatible line item flag. Not used by courier logic. |
| `Lineitem taxable` | No | boolean/string | Compatibility | Shopify-compatible line item tax flag. Not used by courier logic. |
| `Lineitem fulfillment status` | No | string | Compatibility | Shopify-compatible line item fulfillment state. Not used for import decisions. |
| `Billing Name` | No | string | Fallback | Billing customer name. Used as fallback if `Shipping Name` is missing. |
| `Billing Street` | No | string | Fallback | Billing street/address. Preferred fallback for the delivery address when shipping address is missing. |
| `Billing Address1` | No | string | Fallback | Billing address line 1. Used as fallback for delivery address. |
| `Billing Address2` | No | string | Compatibility | Billing address line 2. Kept for Shopify compatibility. |
| `Billing Company` | No | string | Compatibility | Billing company. Not used by courier logic. |
| `Billing City` | No | string | Fallback | Billing city. Used as fallback if `Shipping City` is missing. |
| `Billing Zip` | No | string | Fallback | Billing postal code. Used as fallback if `Shipping Zip` is missing. |
| `Billing Province` | No | string | Compatibility | Billing province/region. Not used by courier logic. |
| `Billing Country` | No | string | Yes | Billing country (ISO code or name). Stored on the order; falls back to `Shipping Country`. |
| `Billing Phone` | No | string | Fallback | Billing phone. Used as fallback if `Shipping Phone` is missing. |
| `Shipping Name` | Yes | string | Yes | Customer full name for delivery and waybill creation. |
| `Shipping Street` | No | string | Fallback | Shipping street/address. Used as fallback/alternative address text. |
| `Shipping Address1` | Yes | string | Yes | Main delivery address text. Required for address validation and office/address suggestions. |
| `Shipping Address2` | No | string | Compatibility | Shipping address line 2. Keep extra details here if needed, but `Shipping Address1` should contain the main address. |
| `Shipping Company` | No | string | Compatibility | Shipping company. Not used by courier logic. |
| `Shipping City` | Yes | string | Yes | Delivery city/settlement. Used for courier city validation. |
| `Shipping Zip` | No | string | Yes | Postal code. Strongly recommended because it improves courier city matching. |
| `Shipping Province` | No | string | Compatibility | Shipping province/region. Not used by courier logic. |
| `Shipping Country` | No | string | Fallback | Shipping country when `Billing Country` is empty. |
| `Shipping Phone` | Yes | string | Yes | Customer delivery phone. Normalized and used for search, customer history, and waybill creation. |
| `Notes` | No | string | Yes | Shopify-style notes. Used for customer notes and can contain pickup-office metadata from some integrations. |
| `Note Attributes` | No | string | Yes | Extra metadata or item properties. Stored with line items where possible and used in shipment notes. |
| `Cancelled at` | No | string | Compatibility | Shopify cancellation timestamp. Not used by the import pipeline for new orders. |
| `Payment Method` | Yes | string | Yes | Payment method text. Use `Cash on Delivery (COD)` for COD orders. For prepaid orders, use the provider/name. |
| `Payment Reference` | No | string | Compatibility | External payment reference. Not used by courier logic. |
| `Refunded Amount` | No | number | Compatibility | Refunded amount. Not used by import logic. |
| `Vendor` | No | string | Compatibility | Product/vendor field. Not used by import logic. |
| `Outstanding Balance` | No | number | Compatibility | Shopify outstanding balance. Not used by import logic. |
| `Employee` | No | string | Compatibility | Shopify POS employee field. Not used by import logic. |
| `Location` | No | string | Compatibility | Shopify location/POS field. Not used by import logic. |
| `Device ID` | No | string | Compatibility | Shopify POS device field. Not used by import logic. |
| `Id` | No | string | Compatibility | External/Shopify order id. Not used as display order code; `Name` is used instead. |
| `Tags` | No | string | Yes | Tags are scanned for risk/report markers and can affect customer verification display. |
| `Risk Level` | No | string | Compatibility | Shopify risk level. Not currently used by courier/import logic. |
| `Source` | No | string | Compatibility | Source channel, for example `api`. Useful for debugging. |
| `Lineitem discount` | No | number | Compatibility | Per-line discount. Currently not used; order-level `Discount Amount` is stored. |
| `Phone` | No | string | Fallback | General phone field. Used as a fallback by API alias parsing when shipping/billing phone is missing. |
| `Courier` | Yes | string | Yes | API-specific column. Required for API stores because courier cannot always be inferred safely from `Shipping Method`. Allowed values: `Speedy`, `Econt`. |

## Required fields summary

The minimum required fields are:

```text
Name, Shipping Name, Shipping Phone, Total, Shipping Method,
Lineitem name, Shipping Address1, Shipping City, Courier, Payment Method
```

`office` is not required and is not part of the canonical payload. If an office
or pickup location is known, include it inside `Shipping Address1` or
`Note Attributes` and set `Shipping Method` to include `to_office`.

## Response — `/orders/import`

```json
{
  "ok": true,
  "imported": 1,
  "skippedExisting": 0,
  "totalReadyForImport": 1
}
```

Orders appear in the dashboard after a successful response.

## Response — `/orders/system`

```json
{
  "ok": true,
  "system": {
    "accepted": 1,
    "parseErrors": []
  }
}
```

This endpoint only accepts data into our system. Import timing is controlled
from the store dashboard (sync from Sheets).

## Errors

```json
{
  "ok": false,
  "error": "Unauthorized."
}
```

Common causes:

- Missing `Authorization` header.
- Invalid or revoked API key.
- Missing required order fields.
- Invalid `items_json`.
- More than 500 orders in one request.

## Security

- Keep API keys server-side.
- Use HTTPS only.
- Revoke old keys from the dashboard when rotating credentials.
- Use one key per external integration when possible.
