From 5c85198e8b72b90280252a14bca26fcd63c205c3 Mon Sep 17 00:00:00 2001 From: vickytechkey Date: Fri, 25 Sep 2026 21:40:57 +0530 Subject: [PATCH] feat: initialize Django shipping service project with Shadowfax API integration and webhook simulator --- .forgejo/workflows/beta.yml | 144 + db.sqlite3 | Bin 0 -> 135168 bytes documentation/forward_integration.md | 563 +++ documentation/reverse_integration.md | 418 +++ documentation/sfx_reverse_pickup_spec.apib | 1894 +++++++++++ documentation/sfx_unified_api_spec.apib | 3025 +++++++++++++++++ manage.py | 22 + requirements.txt | 4 + shipping/__init__.py | 0 shipping/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 154 bytes shipping/__pycache__/admin.cpython-312.pyc | Bin 0 -> 198 bytes shipping/__pycache__/apps.cpython-312.pyc | Bin 0 -> 403 bytes shipping/__pycache__/models.cpython-312.pyc | Bin 0 -> 2308 bytes .../__pycache__/sfx_client.cpython-312.pyc | Bin 0 -> 3804 bytes shipping/__pycache__/urls.cpython-312.pyc | Bin 0 -> 1424 bytes shipping/__pycache__/views.cpython-312.pyc | Bin 0 -> 31183 bytes shipping/admin.py | 3 + shipping/apps.py | 5 + shipping/migrations/0001_initial.py | 35 + shipping/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-312.pyc | Bin 0 -> 2185 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 165 bytes shipping/models.py | 33 + shipping/sfx_client.py | 81 + shipping/static/css/style.css | 294 ++ shipping/templates/shipping/base.html | 44 + shipping/templates/shipping/forward.html | 330 ++ shipping/templates/shipping/logs.html | 183 + shipping/templates/shipping/reverse.html | 266 ++ shipping/templates/shipping/webhooks.html | 176 + shipping/tests.py | 3 + shipping/urls.py | 20 + shipping/views.py | 650 ++++ shippingservice-beta.service | 21 + shippingservice_project/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 169 bytes .../__pycache__/settings.cpython-312.pyc | Bin 0 -> 3304 bytes .../__pycache__/urls.cpython-312.pyc | Bin 0 -> 451 bytes .../__pycache__/wsgi.cpython-312.pyc | Bin 0 -> 689 bytes shippingservice_project/asgi.py | 16 + shippingservice_project/settings.py | 153 + shippingservice_project/urls.py | 7 + shippingservice_project/wsgi.py | 16 + 43 files changed, 8406 insertions(+) create mode 100644 .forgejo/workflows/beta.yml create mode 100644 db.sqlite3 create mode 100644 documentation/forward_integration.md create mode 100644 documentation/reverse_integration.md create mode 100644 documentation/sfx_reverse_pickup_spec.apib create mode 100644 documentation/sfx_unified_api_spec.apib create mode 100755 manage.py create mode 100644 requirements.txt create mode 100644 shipping/__init__.py create mode 100644 shipping/__pycache__/__init__.cpython-312.pyc create mode 100644 shipping/__pycache__/admin.cpython-312.pyc create mode 100644 shipping/__pycache__/apps.cpython-312.pyc create mode 100644 shipping/__pycache__/models.cpython-312.pyc create mode 100644 shipping/__pycache__/sfx_client.cpython-312.pyc create mode 100644 shipping/__pycache__/urls.cpython-312.pyc create mode 100644 shipping/__pycache__/views.cpython-312.pyc create mode 100644 shipping/admin.py create mode 100644 shipping/apps.py create mode 100644 shipping/migrations/0001_initial.py create mode 100644 shipping/migrations/__init__.py create mode 100644 shipping/migrations/__pycache__/0001_initial.cpython-312.pyc create mode 100644 shipping/migrations/__pycache__/__init__.cpython-312.pyc create mode 100644 shipping/models.py create mode 100644 shipping/sfx_client.py create mode 100644 shipping/static/css/style.css create mode 100644 shipping/templates/shipping/base.html create mode 100644 shipping/templates/shipping/forward.html create mode 100644 shipping/templates/shipping/logs.html create mode 100644 shipping/templates/shipping/reverse.html create mode 100644 shipping/templates/shipping/webhooks.html create mode 100644 shipping/tests.py create mode 100644 shipping/urls.py create mode 100644 shipping/views.py create mode 100644 shippingservice-beta.service create mode 100644 shippingservice_project/__init__.py create mode 100644 shippingservice_project/__pycache__/__init__.cpython-312.pyc create mode 100644 shippingservice_project/__pycache__/settings.cpython-312.pyc create mode 100644 shippingservice_project/__pycache__/urls.cpython-312.pyc create mode 100644 shippingservice_project/__pycache__/wsgi.cpython-312.pyc create mode 100644 shippingservice_project/asgi.py create mode 100644 shippingservice_project/settings.py create mode 100644 shippingservice_project/urls.py create mode 100644 shippingservice_project/wsgi.py diff --git a/.forgejo/workflows/beta.yml b/.forgejo/workflows/beta.yml new file mode 100644 index 0000000..46f42c4 --- /dev/null +++ b/.forgejo/workflows/beta.yml @@ -0,0 +1,144 @@ +name: Deploy Beta (NATIVE) + +on: + push: + branches: + - beta + +jobs: + deploy: + runs-on: ubuntu-latest + container: + image: node:22-alpine + steps: + - name: Install git + run: apk add --no-cache git + + - name: Checkout Code + uses: https://github.com/actions/checkout@v4 + + - name: Generate CloudWatch and Nginx Configs Locally + run: | + mkdir -p tmp + cat << 'EOF' > tmp/amazon-cloudwatch-agent.json + { + "agent": { "metrics_collection_interval": 60, "run_as_user": "root" }, + "metrics": { + "metrics_collected": { + "disk": { "measurement": ["used_percent"], "metrics_collection_interval": 60, "resources": ["/"] }, + "mem": { "measurement": ["mem_used_percent"], "metrics_collection_interval": 60 } + } + }, + "logs": { + "logs_collected": { + "files": { + "collect_list": [ + { "file_path": "/home/ubuntu/shippingservice/logs/app.log", "log_group_name": "/shippingservice/app", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/shippingservice/logs/error.log", "log_group_name": "/shippingservice/error", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/shippingservice/logs/gunicorn-beta-access.log", "log_group_name": "/shippingservice/gunicorn-access", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/shippingservice/logs/gunicorn-beta-error.log", "log_group_name": "/shippingservice/gunicorn-error", "log_stream_name": "{instance_id}", "retention_in_days": 30 } + ] + } + } + } + } + EOF + cat << 'EOF' > tmp/nginx-proxy.conf + server { + server_name beta-shipping.tradhox.com shipping.tradhox.com betashipping-api.tradhox.com shippingservice-api.tradhox.com shippingservice.tipro.in; + + location / { + proxy_pass http://127.0.0.1:8086; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + listen 443 ssl; + ssl_certificate /etc/letsencrypt/live/beta-shipping.tradhox.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/beta-shipping.tradhox.com/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + } + + server { + listen 80; + server_name beta-shipping.tradhox.com shipping.tradhox.com betashipping-api.tradhox.com shippingservice-api.tradhox.com shippingservice.tipro.in; + return 301 https://$host$request_uri; + } + EOF + + - name: Package Application + run: | + tar -czf app.tar.gz --exclude=.git --exclude=tmp . + + - name: Deploy to Beta EC2 + run: | + apk add --no-cache openssh-client aws-cli + mkdir -p ~/.ssh + echo "${{ secrets.BETA_EC2_SSH_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + + # Copy app archive and configuration files + scp -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa app.tar.gz tmp/amazon-cloudwatch-agent.json tmp/nginx-proxy.conf shippingservice-beta.service ${{ secrets.BETA_EC2_USERNAME }}@${{ secrets.BETA_EC2_HOST }}:/tmp/ + + # Execute deployment commands on remote server + ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa ${{ secrets.BETA_EC2_USERNAME }}@${{ secrets.BETA_EC2_HOST }} << 'EOF' + set -e + + # ── 2. Install Host Dependencies ────────────────────────────── + sudo apt-get update + sudo apt-get install -y python3-pip python3-venv python3-dev libpq-dev build-essential nginx certbot python3-certbot-nginx + + # ── 3. Extract codebase to target directory ─────────────────── + sudo mkdir -p /home/ubuntu/shippingservice + sudo chown -R ubuntu:ubuntu /home/ubuntu/shippingservice + tar -xzf /tmp/app.tar.gz -C /home/ubuntu/shippingservice + + # ── 4. Set up python virtual environment & dependencies ──────── + cd /home/ubuntu/shippingservice + mkdir -p logs + python3 -m venv venv + ./venv/bin/pip install --upgrade pip + ./venv/bin/pip install -r requirements.txt + + # ── 5. Configure environment variables ───────────────────────── + echo "DEBUG=0" > .env.beta + echo "ENVIRONMENT=beta" >> .env.beta + echo "DJANGO_ENV=beta" >> .env.beta + echo "DB_NAME=shippingservice_beta" >> .env.beta + echo "DB_USER=vignesh" >> .env.beta + echo "DB_PASSWORD=vignesh_secret_pass_2026" >> .env.beta + echo "DB_HOST=16.113.106.152" >> .env.beta + echo "DB_PORT=5432" >> .env.beta + + # ── 6. Run Database Migrations ───────────────────────────────── + ./venv/bin/python manage.py migrate --database=default --noinput + + # ── 7. Configure and restart Systemd service ─────────────────── + sudo mv /tmp/shippingservice-beta.service /etc/systemd/system/shippingservice-beta.service + sudo systemctl daemon-reload + sudo systemctl enable shippingservice-beta + sudo systemctl restart shippingservice-beta + + # ── 8. Configure CloudWatch Agent ────────────────────────────── + sudo mv /tmp/amazon-cloudwatch-agent.json /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent-shippingservice.json || true + sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ + -a fetch-config -m ec2 -s \ + -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent-shippingservice.json || true + + # ── 9. Configure Nginx Reverse Proxy & SSL Cert ──────────────── + sudo mv /tmp/nginx-proxy.conf /etc/nginx/sites-available/shippingservice-beta + sudo ln -sf /etc/nginx/sites-available/shippingservice-beta /etc/nginx/sites-enabled/shippingservice-beta + sudo systemctl restart nginx || true + + if [ ! -d "/etc/letsencrypt/live/beta-shipping.tradhox.com" ]; then + sudo certbot certonly --nginx -d beta-shipping.tradhox.com -d shipping.tradhox.com --non-interactive --agree-tos --register-unsafely-without-email || true + fi + + sudo systemctl restart nginx + + # ── 10. Cleanup tmp files ─────────────────────────────────────── + rm -f /tmp/app.tar.gz + EOF diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..9a7b17ca5c9c8a327a42ee4c14315ad6f21eaf1b GIT binary patch literal 135168 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�+3MQ;A(ixfglYA1lfH~uO3Uzk)64p-HY}@2JA$o^hW9&bcX0;W96;aPHfU>( zW|+PAKW&3zqfEC`_U2}HTd|>;P1*!Nn+z<~YMC_amFe4KXK!&I!~TA>J6fs5w?VU= zy-JOQ7@FGk71=IfTW%~Lnbc^XK+i%yVXZeT&0o6&*8lh1kVl0;00ck)1V8`;KmY_l z00ck)1VEsN0Pg>LkRSmBKmY_l00ck)1V8`;KmY_l00j0N0o?!Zxh6%0KmY_l00ck) z1V8`;KmY_l00cmwhXC&XdypUj1V8`;KmY_l00ck)1V8`;KmY{x90A<_@3|&Lg+KrV zKmY_l00ck)1V8`;KmY_lpoc(&Qo`b`fcQ=E8{!{}zc0QgzDuWgfdB}A00@8p2!H?x zfB*=900@8p2s{dbSU40oezN_83jD{W?4My6AEFDB&M&c<&fm$wP+;by^K&NF-KJ)G z-j9Ytfr$zB%SG%bSq=y3Y?__9KU5SMptKlEwQ!`9W zx~i96Z#C)kR!cLj4a;hpXXoe3YE?U9T53hFSI(FlYPoTHUA=Qgug`C$=Sx+co(MJP z4Lgr{^~&0{Hx5cte32d>Z5a9wsg~ZT$InXfYmL{nx@4-gW>r%hcZ$Ulm1HWN$;MB! zY)g&0MGoh#(Nn7|m)dMr^%8e7f72vS2jArG?Q#{TSVb>0W2)8MmDO`|DLIv!TUd~j zwoiIowxw2;+nQe4u*fbe%V%U>+O3jO)+|-8nv~4^k^)LAZD^&}DaA5c8ndI+qM_Ey zN?omSRcnn_y}Z;gG+qE*rYC82Dx%)16*YrNSzDPa6c!ee$y6$xj+2i!555`rGM7$u zw_2>gxY;U|G}E+ImQ_o2ReXs0QSs{SqPOf9U!7wp3WelCDxc1EM0N_UYquRu=Gyh8 zrRB9Xj!Z7hrLs~odp46kE2qz-GYh#~>i+Zqb)RQjtoxj&?h~f&^Wv4&R~A>#Io;>7 zYP_yl&8k|`q$@^QGd@($X|NpT{CBJ4@ZuEqP&8|Snk(II1^aSOr*N9XzG`W{q|?`^Et*E7+$vehrdn;WGLo4>!R~JCqI$PR-CLVm{nJ zw5A!GdP!4@dX=7BmzLNN+eJS~c4inFVjc(c&pGGMYIm9j4WcKTc0uN|*_d`6L^H{wnrb8m}|pO)e!!oyNt(CQWH zLPm=vb}C **Document Scope**: End-to-End Technical Integration Guide for Shadowfax Logistics Platform +> **Supported Models**: Marketplace Seller Pickup & Warehouse Hub Dispatch +> **API Version**: Unified API v1 / v3 / v4 +> **Document Status**: Production Ready + +--- + +## 1. Executive Summary & Architecture + +Shadowfax (SFX) Unified API offers a single, standardized REST interface designed to streamline forward logistics operations across e-commerce marketplaces, brand storefronts, quick-commerce networks, and regional fulfillment hubs. The platform automates every stage of the shipment lifecycle—from pincode serviceability and pre-allocated AWB numbers to multi-location order placement, real-time shipment tracking, active escalation handling, digital proof of delivery (POD), dynamic COD QR codes, and real-time webhook callback notifications. + +### Operational Models Breakdown +1. **Marketplace Model (Seller Pickup & Delivery)**: + - **First Mile**: Shadowfax delivery partners pick up shipments directly from distributed seller stores, retail outlets, or micro-warehouses. + - **Middle Mile**: Shipments are routed via local Origin Hubs and linehaul Distribution Centers (DCs). + - **Last Mile**: Delivered to the end customer address. + - **Reverse / RTS**: If delivery fails after max attempts or customer rejection, the order triggers Return to Seller (RTS) and is returned to the origin pickup location. +2. **Warehouse Model (Hub Dispatch & Delivery)**: + - **First Mile / Linehaul**: Shipments originate directly from centralized client warehouses or regional distribution hubs and are handed over to Shadowfax. + - **Last Mile**: Processed through Destination Hubs and delivered to the end customer. + - **Reverse / RTO**: If undeliverable, shipments enter Return to Origin (RTO) and return to the client warehouse. + +### Environments & Host URLs +| Environment | Base URL | Purpose | +| :--- | :--- | :--- | +| **Staging / Sandbox** | `https://dale.staging.shadowfax.in` | Developer integration, QA testing & test pincodes (`110009`, `560077`, `560007`) | +| **Production** | `https://flash.shadowfax.in` | Live order processing & operational delivery management | + +--- + +## 2. Technical Glossary & Core Logistics Concepts + +### Facility Network Architecture +- **Distribution Center (DC)**: Large sorting facilities processing bulk intracity and intercity linehaul shipments. +- **Hub**: Neighborhood-level delivery centers managing rider assignment for last-mile delivery and first-mile seller pickup. +- **Origin Hub**: The initial Shadowfax facility receiving packages directly from sellers or micro-warehouses. +- **Origin DC**: The regional distribution center receiving packages from Origin Hubs or client primary warehouses. +- **Destination DC**: The sorting hub in the recipient city prior to local hub distribution. +- **Destination Hub**: The final delivery hub from which last-mile riders execute customer delivery. +- **Via DC**: Intermediate transit sorting hubs where manifest bags pass through without unbagging. + +### Logistics Identifiers & Artifacts +- **Air Waybill (AWB)**: Unique 10-14 digit tracking identification number assigned to every parcel. +- **Letter of Receipt (LOR)**: Formal digital acknowledgement issued during physical handover of bulk parcels. +- **Proof of Delivery (POD)**: Digital evidence (recipient signature, photo timestamp, or OTP) confirming delivery. +- **COD / Cash on Delivery**: Collect-on-delivery payments collected via cash or dynamic UPI QR codes. + +--- + +## 3. Authentication & Global HTTP Specifications + +Shadowfax API uses **HTTP Header Token Authentication**. Production and Staging API tokens are provisioned by the Shadowfax Integration Team. + +### Mandatory Request Headers +```http +Authorization: Token +Content-Type: application/json +Accept: application/json +``` + +### Standard HTTP Status Codes +| HTTP Code | Category | Meaning & Operational Handling | +| :--- | :--- | :--- | +| `200 OK` | Success | Request succeeded. Response body contains order status or payload data. | +| `201 Created` | Success | Order created or AWB generated successfully. | +| `400 Bad Request` | Client Error | Schema validation failure or missing mandatory fields. Check field types. | +| `401 Unauthorized` | Security Error | Missing or invalid API Token in `Authorization` header. | +| `404 Not Found` | Resource Error | AWB number or Order ID does not exist in Shadowfax system. | +| `422 Unprocessable` | Business Error | Non-serviceable pincode, duplicate `client_order_id`, or invalid state transition. | +| `500 Server Error` | System Error | Downstream system unavailable; execute exponential backoff retry. | + +--- + +## 4. Order Lifecycle & State Transitions + +### 4.1 Marketplace Order Lifecycle States +| State Name | Status Description | Next Valid States | +| :--- | :--- | :--- | +| `new` | Order created in Shadowfax system; awaiting seller pickup assignment. | `pickup_assigned`, `cancelled` | +| `pickup_assigned` | Rider assigned for seller pickup. | `pickup_rescheduled`, `picked_up`, `pickup_cancelled` | +| `pickup_rescheduled` | Seller requested pickup deferral / pickup hold. | `pickup_assigned`, `pickup_cancelled` | +| `picked_up` | Package collected from seller; in transit to Origin Hub. | `in_transit` | +| `in_transit` | Moving between Origin Hub, DCs, and Destination Hub. | `out_for_delivery` | +| `out_for_delivery` | Package with last-mile delivery rider for customer attempt. | `delivered`, `delivery_rescheduled`, `rts_initiated` | +| `delivery_rescheduled` | Delivery attempt failed or customer requested date change. | `out_for_delivery`, `rts_initiated` | +| `delivered` | Package successfully delivered to customer. | *Terminal State* | +| `cancelled` | Order cancelled prior to pickup. | *Terminal State* | +| `rts_initiated` | Return to Seller initiated after max delivery attempts or rejection. | `rts_in_transit` | +| `rts_in_transit` | Package returning to seller location. | `rts_out_for_delivery` | +| `rts_out_for_delivery` | Rider out to hand over returned package back to seller. | `rts_delivered` | +| `rts_delivered` | Package successfully handed back to seller. | *Terminal State* | + +### 4.2 Warehouse Order Lifecycle States +| State Name | Status Description | Next Valid States | +| :--- | :--- | :--- | +| `new` | Order created for warehouse fulfillment. | `in_transit`, `cancelled` | +| `in_transit` | In transit from client warehouse to destination hub. | `out_for_delivery` | +| `out_for_delivery` | Package out for customer delivery attempt. | `delivered`, `delivery_rescheduled`, `rto_initiated` | +| `delivery_rescheduled` | Customer requested alternate delivery slot or delivery hold. | `out_for_delivery`, `rto_initiated` | +| `delivered` | Successfully delivered to customer. | *Terminal State* | +| `cancelled` | Order cancelled before dispatch. | *Terminal State* | +| `rto_initiated` | Return to Origin initiated. | `rto_in_transit` | +| `rto_in_transit` | Package moving back to client warehouse. | `rto_delivered` | +| `rto_delivered` | Package returned and verified at client warehouse. | *Terminal State* | + +--- + +## 5. Push Callback API (Webhooks) + +Shadowfax provides real-time event notifications via HTTP POST webhooks pushed directly to the client's configured endpoint whenever a shipment transitions state. + +### Webhook Request Headers +```http +Content-Type: application/json +X-Shadowfax-Signature: +``` + +### Webhook Standard Payload Schema +```json +{ + "client_order_id": "ORD-2026-99012", + "awb_number": "SFX10928374651", + "status": "out_for_delivery", + "sub_status": "out_for_delivery", + "remarks": "Package out for delivery with rider Rajesh (Ph: +919876543210)", + "reason_code": "", + "timestamp": "2026-09-25T14:30:00Z", + "location": { + "latitude": 12.9716, + "longitude": 77.5946, + "city": "Bengaluru" + }, + "attempt_count": 1, + "pickup_details": { + "pickup_id": "PICKUP-8812", + "name": "Acme Electronics Hub" + } +} +``` + +### Webhook Event State Mappings +| Webhook `status` | Marketplace State Equivalent | Warehouse State Equivalent | Trigger Event | +| :--- | :--- | :--- | :--- | +| `pickup_assigned` | `pickup_assigned` | N/A | Pickup executive assigned to seller. | +| `picked_up` | `picked_up` | N/A | Package scanned at seller location. | +| `in_transit` | `in_transit` | `in_transit` | Package arrived or departed DC/Hub. | +| `out_for_delivery` | `out_for_delivery` | `out_for_delivery` | Package scanned onto rider runsheet. | +| `delivered` | `delivered` | `delivered` | Successful delivery to customer. | +| `delivery_rescheduled`| `delivery_rescheduled`| `delivery_rescheduled`| Delivery attempt deferred with reason code. | +| `rts_initiated` | `rts_initiated` | N/A | Return to seller triggered. | +| `rto_initiated` | N/A | `rto_initiated` | Return to origin warehouse triggered. | + +--- + +## 6. Endpoints Reference & Full API Specifications + +### 6.1 Serviceability API +**HTTP Method**: `GET` +**Endpoint Path**: `/v1/clients/serviceability/?service={service}&page={page}&count={count}&pincodes={pincodes}` +**Description**: Fetches list of serviceable pincodes or verifies if a specific destination pincode is active for delivery/pickup. + +#### Query Parameters +| Parameter | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| `service` | String | No | Service filter (e.g. `express`, `surface`, `reverse`). | +| `pincodes` | String | No | Comma-separated list of 6-digit Indian pincodes to query. | +| `page` | Integer | No | Page number for paginated pincode index (Default: `1`). | +| `count` | Integer | No | Number of records per page (Default: `100`, Max: `1000`). | + +#### Sample Response Payload (`200 OK`) +```json +{ + "status": "success", + "message": "Serviceability retrieved successfully", + "data": { + "total_count": 2, + "page": 1, + "pincodes": [ + { + "pincode": "560077", + "city": "Bengaluru", + "state": "Karnataka", + "serviceable": true, + "pickup_available": true, + "delivery_available": true, + "cod_available": true, + "prepaid_available": true + }, + { + "pincode": "110009", + "city": "New Delhi", + "state": "Delhi", + "serviceable": true, + "pickup_available": true, + "delivery_available": true, + "cod_available": true, + "prepaid_available": true + } + ] + } +} +``` + +### 6.2 AWB Generation API +**HTTP Method**: `POST` +**Endpoint Path**: `/v3/clients/generate_marketplace_awb/` +**Description**: Pre-allocates a Shadowfax AWB tracking number prior to creating the complete order payload. + +#### Sample Request Payload +```json +{ + "client_code": "ACME_RETAIL", + "count": 1 +} +``` + +#### Sample Response Payload (`200 OK`) +```json +{ + "status": "success", + "data": { + "awb_numbers": [ + "SFX9928374102" + ] + } +} +``` + +### 6.3 Marketplace Order Creation API (Seller Pickup Model) +**HTTP Method**: `POST` +**Endpoint Path**: `/v3/clients/orders/` +**Description**: Creates a seller pickup request and customer delivery order in a single API call. + +#### Request Payload Parameter Matrix +##### 1. `order_details` Object +| Field Name | Type | Required | Validation & Description | +| :--- | :--- | :--- | :--- | +| `client_order_id` | String | **Yes** | Client internal order ID (Must be unique across system). | +| `awb_number` | String | No | Pre-generated Shadowfax AWB number. Auto-generated if omitted. | +| `actual_weight` | Number | **Yes** | Weight of shipment in grams (e.g. `500.0`). | +| `volumetric_weight` | Number | No | Volumetric weight in grams `(L * W * H) / 5000` (Default: `0.0`). | +| `product_value` | Number | **Yes** | Total invoice product value in INR excluding tax. | +| `payment_mode` | String | **Yes** | Payment type: `"Prepaid"` or `"COD"`. | +| `cod_amount` | Number | **Yes** | COD amount to collect from recipient (`0` if `Prepaid`). | +| `promised_delivery_date` | String | No | Expected delivery date format `YYYY-MM-DD`. | + +##### 2. `customer_details` Object +| Field Name | Type | Required | Validation & Description | +| :--- | :--- | :--- | :--- | +| `name` | String | **Yes** | Recipient full name. | +| `phone` | String | **Yes** | 10-digit mobile number of recipient. | +| `address_line_1` | String | **Yes** | Building, street, flat number. | +| `address_line_2` | String | No | Landmark, locality. | +| `pincode` | String | **Yes** | 6-digit serviceable delivery pincode. | +| `city` | String | **Yes** | Destination city. | +| `state` | String | **Yes** | Destination state. | + +##### 3. `pickup_details` Object +| Field Name | Type | Required | Validation & Description | +| :--- | :--- | :--- | :--- | +| `name` | String | **Yes** | Seller / Store / Warehouse name. | +| `phone` | String | **Yes** | 10-digit contact mobile number of seller. | +| `address_line_1` | String | **Yes** | Seller warehouse address. | +| `pincode` | String | **Yes** | 6-digit origin pickup pincode. | +| `city` | String | **Yes** | Origin city. | +| `state` | String | **Yes** | Origin state. | + +##### 4. `rts_details` Object (Return to Seller) +| Field Name | Type | Required | Validation & Description | +| :--- | :--- | :--- | :--- | +| `name` | String | **Yes** | Return contact name. | +| `phone` | String | **Yes** | Return contact phone number. | +| `address_line_1` | String | **Yes** | Return seller address. | +| `pincode` | String | **Yes** | 6-digit return pincode. | + +##### 5. `product_details` Array Items +| Field Name | Type | Required | Validation & Description | +| :--- | :--- | :--- | :--- | +| `sku_name` | String | **Yes** | Item title or product name. | +| `sku_code` | String | **Yes** | Merchant SKU code. | +| `price` | Number | **Yes** | Individual SKU price. | +| `quantity` | Integer | **Yes** | Quantity of items. | + +#### Complete Marketplace Request Payload Example +```json +{ + "order_type": "marketplace", + "order_details": { + "client_order_id": "ORD-MKT-2026-101", + "awb_number": "SFX9928374102", + "actual_weight": 750.0, + "volumetric_weight": 800.0, + "product_value": 1499.0, + "payment_mode": "COD", + "cod_amount": 1499.0 + }, + "customer_details": { + "name": "Ananya Sharma", + "phone": "9876543210", + "address_line_1": "Flat 402, Sunshine Apartments", + "address_line_2": "Indiranagar 10th Main", + "pincode": "560038", + "city": "Bengaluru", + "state": "Karnataka" + }, + "pickup_details": { + "name": "StyleHub Retail Store", + "phone": "9123456789", + "address_line_1": "Plot 12, Industrial Area Phase 2", + "pincode": "560077", + "city": "Bengaluru", + "state": "Karnataka" + }, + "rts_details": { + "name": "StyleHub Returns Center", + "phone": "9123456789", + "address_line_1": "Plot 12, Industrial Area Phase 2", + "pincode": "560077", + "city": "Bengaluru", + "state": "Karnataka" + }, + "product_details": [ + { + "sku_name": "Wireless Noise Cancelling Earbuds", + "sku_code": "SKU-EAR-001", + "price": 1499.0, + "quantity": 1 + } + ] +} +``` + +#### Sample Response Payload (`201 Created`) +```json +{ + "status": "success", + "message": "Order created successfully", + "data": { + "client_order_id": "ORD-MKT-2026-101", + "awb_number": "SFX9928374102", + "order_status": "new", + "created_at": "2026-09-25T14:35:00Z" + } +} +``` + +### 6.4 Warehouse Order Creation API (Hub Dispatch Model) +**HTTP Method**: `POST` +**Endpoint Path**: `/v3/clients/orders/` +**Description**: Creates a delivery order for shipments dispatched directly from a client warehouse or hub. + +#### Complete Warehouse Request Payload Example +```json +{ + "order_type": "warehouse", + "order_details": { + "client_order_id": "ORD-WH-2026-505", + "awb_number": "SFX8817263541", + "actual_weight": 1200.0, + "product_value": 3500.0, + "payment_mode": "Prepaid", + "cod_amount": 0.0 + }, + "customer_details": { + "name": "Vikram Mehta", + "phone": "9988776655", + "address_line_1": "House 14, Civil Lines", + "pincode": "110054", + "city": "New Delhi", + "state": "Delhi" + }, + "rto_details": { + "name": "Central Warehouse North", + "phone": "9811002233", + "address_line_1": "Warehouse Complex Hub 3", + "pincode": "110042", + "city": "New Delhi", + "state": "Delhi" + }, + "product_details": [ + { + "sku_name": "Mechanical Keyboard RGB", + "sku_code": "SKU-KB-102", + "price": 3500.0, + "quantity": 1 + } + ] +} +``` + +### 6.5 Single Order Tracking API (V4) +**HTTP Method**: `GET` +**Endpoint Path**: `/v4/clients/orders/{awb_number}/track/` +**Description**: Retrieves comprehensive tracking timeline, current location, rider details, and status for a shipment. + +#### Sample Response Payload (`200 OK`) +```json +{ + "status": "success", + "data": { + "client_order_id": "ORD-MKT-2026-101", + "awb_number": "SFX9928374102", + "current_status": "out_for_delivery", + "expected_delivery_date": "2026-09-25", + "rider_details": { + "name": "Rajesh Kumar", + "phone": "+919876543210" + }, + "tracking_history": [ + { + "status": "new", + "location": "Bengaluru Hub", + "timestamp": "2026-09-24T10:00:00Z", + "remarks": "Order created" + }, + { + "status": "picked_up", + "location": "StyleHub Store", + "timestamp": "2026-09-24T14:30:00Z", + "remarks": "Package picked up from seller" + }, + { + "status": "in_transit", + "location": "Bengaluru Main DC", + "timestamp": "2026-09-24T19:00:00Z", + "remarks": "Arrived at sorting DC" + }, + { + "status": "out_for_delivery", + "location": "Indiranagar Hub", + "timestamp": "2026-09-25T08:30:00Z", + "remarks": "Assigned to rider for delivery" + } + ] + } +} +``` + +### 6.6 Bulk Order Tracking API (V4) +**HTTP Method**: `POST` +**Endpoint Path**: `/v4/clients/bulk_track/` +**Description**: Fetches current tracking status for up to 50 AWB numbers in a single request. + +#### Sample Request Payload +```json +{ + "awb_numbers": [ + "SFX9928374102", + "SFX8817263541" + ] +} +``` + +### 6.7 Order Cancellation API +**HTTP Method**: `POST` +**Endpoint Path**: `/v3/clients/orders/cancel/` +**Description**: Cancels a pending order prior to rider pickup or warehouse dispatch. + +#### Sample Request Payload +```json +{ + "client_order_id": "ORD-MKT-2026-101", + "awb_number": "SFX9928374102", + "reason": "Customer requested cancellation" +} +``` + +### 6.8 Escalation & Support Ticket API +**HTTP Method**: `POST` +**Endpoint Path**: `/v1/clients/support/issue/` +**Description**: Raises operational support tickets directly to Shadowfax ground control team. + +#### Issue Categories Available +- `DELAYED_DELIVERY`: Delivery SLA breached or delayed. +- `EXPEDITE_PICKUP`: Urgent seller pickup required. +- `DELIVERY_DISPUTE`: Customer disputes status marked delivered. +- `ADDRESS_UPDATE`: Correcting destination address/phone. + +#### Sample Request Payload +```json +{ + "awb_number": "SFX9928374102", + "category": "EXPEDITE_PICKUP", + "description": "Seller store closing early today at 4 PM, please pick up by 3 PM." +} +``` + +### 6.9 Proof of Delivery (POD) API +**HTTP Method**: `POST` +**Endpoint Path**: `/v1/clients/pod_details/` +**Description**: Retrieves digital signature image URL, photo proof, or OTP verification details for delivered shipments. + +#### Sample Response Payload (`200 OK`) +```json +{ + "status": "success", + "data": { + "awb_number": "SFX9928374102", + "delivery_timestamp": "2026-09-25T11:45:00Z", + "signature_url": "https://cdn.shadowfax.in/pod/signatures/SFX9928374102.png", + "photo_proof_url": "https://cdn.shadowfax.in/pod/photos/SFX9928374102.jpg", + "receiver_name": "Ananya Sharma", + "otp_verified": true + } +} +``` + +### 6.10 Dynamic QR Code Generation API (V2) +**HTTP Method**: `POST` +**Endpoint Path**: `/v2/clients/generate_qr_code/` +**Description**: Generates dynamic UPI payment QR code strings for Cash-on-Delivery collection at customer doorstep. + +#### Sample Response Payload (`200 OK`) +```json +{ + "status": "success", + "data": { + "awb_number": "SFX9928374102", + "cod_amount": 1499.0, + "qr_code_string": "upi://pay?pa=shadowfax@icici&pn=Shadowfax&am=1499.00&tr=SFX9928374102&tn=COD%20Payment", + "qr_code_image_base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." + } +} +``` + +--- + +## 7. Appendix: Standard Reason & Remark Codes Catalog + +Shadowfax uses standardized numerical and string remark codes during exception events. Client system webhooks should parse these codes for automated business logic. + +### 7.1 Delivery On-Hold Remark Codes +| Reason Code | Code Description | Suggested Operational Handling | +| :--- | :--- | :--- | +| `OH_ADDR_INCOMPLETE` | Customer address incomplete or missing landmark. | Trigger SMS/WhatsApp to customer to update address. | +| `OH_CUST_UNAVAILABLE` | Customer not available at address. | Reschedule for next business day automatically. | +| `OH_COD_NOT_READY` | Customer requested delivery when cash/UPI ready. | Reschedule delivery date per customer request. | +| `OH_PINCODE_UNMAPPED` | Destination pincode out of delivery hub zone. | Trigger escalation ticket to reassign hub. | + +### 7.2 Pickup On-Hold Remark Codes +| Reason Code | Code Description | Suggested Operational Handling | +| :--- | :--- | :--- | +| `PU_STORE_CLOSED` | Seller store/warehouse closed during pickup attempt. | Notify seller contact via alert. | +| `PU_ITEM_NOT_READY` | Shipment packaging incomplete at seller end. | Reschedule seller pickup window. | +| `PU_ADDR_NOT_FOUND` | Seller pickup address untraceable. | Verify pickup coordinates and phone number. | + +### 7.3 Cancellation Reason Codes +| Reason Code | Code Description | Originator | +| :--- | :--- | :--- | +| `CAN_CLIENT_REQ` | Order cancelled by client system via API. | Client | +| `CAN_SELLER_DENIED` | Seller denied stock availability during pickup. | Seller | +| `CAN_EXCEEDED_ATTEMPTS`| Max delivery attempts reached (RTS/RTO triggered).| Courier | + +--- + +## 8. Summary & Next Steps for Integration + +1. **Credential Setup**: Obtain Staging API Token and test pincode list from Shadowfax Integration Team. +2. **Webhook Endpoint Registration**: Configure HTTPS POST Webhook listener and verify HMAC signature parsing. +3. **End-to-End Sandbox Testing**: Execute Pincode Check $\rightarrow$ Order Creation $\rightarrow$ Tracking $\rightarrow$ Webhook Receive $\rightarrow$ POD retrieval flow. +4. **Go-Live Readiness Review**: Obtain sign-off from Shadowfax Logistics Operations for Production URL switch. \ No newline at end of file diff --git a/documentation/reverse_integration.md b/documentation/reverse_integration.md new file mode 100644 index 0000000..ea99180 --- /dev/null +++ b/documentation/reverse_integration.md @@ -0,0 +1,418 @@ +# Shadowfax Reverse Pickup API (SFX) — Complete Reverse Integration Document + +> **Document Scope**: Technical Specification & Integration Guide for Shadowfax Reverse Logistics & Customer Return Platform +> **Core Capability**: Doorstep Pickups, Quality Check (QC) Enforcement, Warehouse Returns & Callback Synchronization +> **API Version**: Reverse API v1 / v2 / v3 / v4 +> **Document Status**: Production Ready + +--- + +## 1. System Architecture & Reverse Logistics Overview + +The Shadowfax (SFX) Reverse Pickup API enables e-commerce merchants, marketplace operators, and brand retailers to automate customer return pickups and doorstep Quality Checks (QC). Unlike forward delivery where parcels originate from seller/warehouse locations, reverse logistics manages customer doorstep pickups, item inspection, hub consolidation, linehaul transport, and final delivery back to the client's return warehouse. + +### Key Operational Capabilities +1. **Doorstep Customer Pickup**: Delivery executive collects items directly from the customer's specified pickup address. +2. **Configurable Quality Check (QC)**: Executive verifies physical item condition, brand, serial/IMEI numbers, original packaging, and tags prior to accept/reject. +3. **Reverse Linehaul & Consolidation**: Collected items are routed through Shadowfax Origin Hubs and linehaul Distribution Centers (DCs). +4. **Warehouse Return Delivery**: Final delivery and digital handover of collected return inventory to client fulfillment centers. +5. **Real-time Callback Synchronization**: Push webhooks notify client systems instantly upon pickup status change, QC pass/fail events, and warehouse delivery. + +### Environment Base URLs +| Environment | Base URL | Purpose | +| :--- | :--- | :--- | +| **Staging / Sandbox** | `https://dale.staging.shadowfax.in` | Integration testing & test pincodes (`110009`, `560077`, `560007`) | +| **Production** | `https://flash.shadowfax.in` | Live reverse order creation & operational execution | + +--- + +## 2. Technical Glossary & Reverse Network Facilities + +### Facility Network Architecture +- **Pickup Customer Location**: The customer residential or commercial address where the return item is collected. +- **Origin Hub**: First Shadowfax local hub receiving the picked-up return item from the delivery rider. +- **Origin DC**: Primary sorting Distribution Center processing reverse linehaul routing. +- **Destination DC**: Sorting center located near the client return warehouse. +- **Destination Hub / Return Hub**: Final Shadowfax hub executing bulk delivery back to the client warehouse. +- **Client Return Warehouse**: Final destination facility where returned goods are handed over to the merchant. + +### Reverse Logistics Identifiers +- **Client Request ID / Order ID**: Client's unique internal reference for the return request. +- **Reverse Air Waybill (AWB)**: Unique tracking number assigned to the reverse shipment. +- **QC Parameter**: Inspection rules (e.g., brand check, IMEI match, unused status) defined per item. +- **Proof of Pickup (POP) / POD**: Digital signature or photo timestamp confirming return item collection. + +--- + +## 3. Authentication & Global HTTP Specifications + +Shadowfax API uses **HTTP Header Token Authentication**. Production and Staging API tokens are provisioned by the Shadowfax Integration Team. + +### Mandatory HTTP Headers +```http +Authorization: Token +Content-Type: application/json +Accept: application/json +``` + +### Standard HTTP Status Codes +| HTTP Code | Category | Operational Meaning & Action | +| :--- | :--- | :--- | +| `200 OK` | Success | Request succeeded. Body contains request details or query results. | +| `201 Created` | Success | Reverse pickup request successfully created. | +| `400 Bad Request` | Validation Error | Missing mandatory field or invalid data type format. | +| `401 Unauthorized` | Security Error | Invalid or missing API token in `Authorization` header. | +| `404 Not Found` | Resource Error | Client Request ID or Reverse AWB number does not exist. | +| `422 Unprocessable` | Business Rule | Non-serviceable pickup pincode or duplicate `client_request_id`. | +| `500 Server Error` | System Exception | Downstream system unavailable; execute retry with exponential backoff. | + +--- + +## 4. Reverse Order Lifecycle & Quality Check (QC) Architecture + +### 4.1 Reverse Pickup Lifecycle States +| State Name | Status Description | Next Valid States | +| :--- | :--- | :--- | +| `new` | Pickup request created in Shadowfax system; awaiting rider assignment. | `pickup_assigned`, `cancelled` | +| `pickup_assigned` | Rider assigned to visit customer address. | `picked_up`, `qc_failed`, `pickup_rescheduled`, `cancelled` | +| `pickup_rescheduled` | Customer requested alternate pickup date/time window. | `pickup_assigned`, `cancelled` | +| `picked_up` | Item passed QC checks and collected from customer; in transit to Origin Hub. | `in_transit` | +| `qc_failed` | Item failed doorstep QC checks (e.g. wrong brand, damaged item). Pickup rejected. | *Terminal State* | +| `in_transit` | Item moving between Shadowfax Hubs and linehaul DCs toward client warehouse. | `out_for_delivery_to_warehouse` | +| `out_for_delivery_to_warehouse` | Item assigned to rider for bulk handover at client return warehouse. | `delivered_to_warehouse`, `delivery_rescheduled` | +| `delivered_to_warehouse` | Return item verified and received at client warehouse. | *Terminal State* | +| `cancelled` | Reverse pickup request cancelled prior to pickup execution. | *Terminal State* | + +### 4.2 Quality Check (QC) Enforcement Rules +Shadowfax riders perform doorstep QC based on criteria supplied in the order payload: +1. **Brand & Model Match**: Verifies item brand name and model match order manifest. +2. **Serial / IMEI Verification**: Matches 15-digit mobile IMEI or product serial number. +3. **Physical Damage Inspection**: Checks for cracks, dents, water damage, or missing components. +4. **Tag / Packaging Verification**: Confirms original brand tags/box are intact if required. +5. **Doorstep Image Capture**: Rider uploads photo proof of item condition during pickup. + +--- + +## 5. Push Callback API (Webhooks) + +Shadowfax notifies client systems in real time whenever a reverse pickup transitions state. + +### Webhook Request Headers +```http +Content-Type: application/json +X-Shadowfax-Signature: +``` + +### Webhook Standard Payload Schema +```json +{ + "client_request_id": "REV-ORD-2026-8801", + "awb_number": "SFX-REV-99182736", + "status": "picked_up", + "sub_status": "qc_passed", + "remarks": "Item passed physical QC and picked up by rider Ramesh", + "reason_code": "", + "qc_result": { + "qc_passed": true, + "brand_matched": true, + "imei_matched": true, + "serial_number": "SN-9988112233", + "images": [ + "https://cdn.shadowfax.in/qc/photos/REV-ORD-2026-8801_1.jpg" + ] + }, + "timestamp": "2026-09-25T15:10:00Z" +} +``` + +--- + +## 6. API Endpoints Reference & Complete Technical Specifications + +### 6.1 Reverse Pincode Serviceability API +**HTTP Method**: `GET` +**Endpoint Path**: `/v1/clients/serviceability/?service=reverse&page={page}&count={count}&pincodes={pincodes}` +**Description**: Verifies if customer pickup pincodes are active for reverse pickup operations. + +#### Query Parameters +| Parameter | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| `service` | String | **Yes** | Pass `"reverse"` for reverse pickup serviceability query. | +| `pincodes` | String | No | Comma-separated 6-digit Indian pincodes to check. | +| `page` | Integer | No | Page number for paginated results (Default: `1`). | +| `count` | Integer | No | Records per page (Default: `100`). | + +#### Sample Response Payload (`200 OK`) +```json +{ + "status": "success", + "data": { + "total_count": 1, + "pincodes": [ + { + "pincode": "560077", + "city": "Bengaluru", + "state": "Karnataka", + "reverse_pickup_available": true, + "max_weight_grams": 5000.0 + } + ] + } +} +``` + +### 6.2 Reverse AWB Generation API +**HTTP Method**: `POST` +**Endpoint Path**: `/v3/clients/orders/generate_awb/` +**Description**: Pre-allocates tracking numbers for reverse pickup shipments. + +#### Sample Request Payload +```json +{ + "client_code": "ACME_RETAIL", + "count": 1 +} +``` + +#### Sample Response Payload (`200 OK`) +```json +{ + "status": "success", + "data": { + "awb_numbers": [ + "SFX-REV-99182736" + ] + } +} +``` + +### 6.3 Reverse Pickup Request Creation API +**HTTP Method**: `POST` +**Endpoint Path**: `/v3/clients/requests` +**Description**: Creates a customer doorstep pickup request with embedded QC parameters. + +#### Request Payload Parameter Matrix +##### 1. Root Level & Order Identification +| Field Name | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| `client_request_id` | String | **Yes** | Client internal unique return request identifier. | +| `awb_number` | String | No | Pre-allocated reverse AWB number. Auto-generated if omitted. | +| `actual_weight` | Number | **Yes** | Expected weight of return item in grams. | +| `product_value` | Number | **Yes** | Invoice price of item being returned in INR. | +| `pickup_date` | String | No | Scheduled pickup date format `YYYY-MM-DD`. | + +##### 2. `pickup_details` (Customer Location) +| Field Name | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| `name` | String | **Yes** | Customer full name. | +| `phone` | String | **Yes** | 10-digit mobile number of customer. | +| `address_line_1` | String | **Yes** | Customer street address and flat/house number. | +| `pincode` | String | **Yes** | 6-digit serviceable pickup pincode. | +| `city` | String | **Yes** | Pickup city. | +| `state` | String | **Yes** | Pickup state. | + +##### 3. `return_details` (Client Warehouse Destination) +| Field Name | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| `name` | String | **Yes** | Warehouse return hub name. | +| `phone` | String | **Yes** | Contact phone number of return warehouse. | +| `address_line_1` | String | **Yes** | Warehouse street address. | +| `pincode` | String | **Yes** | 6-digit warehouse pincode. | +| `city` | String | **Yes** | Warehouse city. | +| `state` | String | **Yes** | Warehouse state. | + +##### 4. `qc_details` Object (Quality Check Instructions) +| Field Name | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| `qc_check` | Boolean | **Yes** | `true` to enforce doorstep QC inspection. | +| `brand_name` | String | No | Expected brand name on item label. | +| `item_name` | String | **Yes** | SKU name / description. | +| `serial_number` | String | No | Expected product serial or IMEI number. | +| `image_required` | Boolean | No | `true` if rider must take photo proof during QC. | + +#### Complete Reverse Pickup Request Payload Example +```json +{ + "client_request_id": "REV-ORD-2026-8801", + "awb_number": "SFX-REV-99182736", + "actual_weight": 500.0, + "product_value": 2999.0, + "pickup_details": { + "name": "Kavita Reddy", + "phone": "9876501234", + "address_line_1": "Apartment 102, Green Glen Layout", + "address_line_2": "Bellandur", + "pincode": "560103", + "city": "Bengaluru", + "state": "Karnataka" + }, + "return_details": { + "name": "Acme Central Returns Warehouse", + "phone": "9811004455", + "address_line_1": "Logistics Park Warehouse 4B", + "pincode": "560077", + "city": "Bengaluru", + "state": "Karnataka" + }, + "qc_details": { + "qc_check": true, + "brand_name": "SoundCore", + "item_name": "Over-Ear Headphones Black", + "serial_number": "SN-9988112233", + "image_required": true + } +} +``` + +#### Sample Response Payload (`201 Created`) +```json +{ + "status": "success", + "message": "Reverse pickup request created successfully", + "data": { + "client_request_id": "REV-ORD-2026-8801", + "awb_number": "SFX-REV-99182736", + "status": "new", + "created_at": "2026-09-25T15:00:00Z" + } +} +``` + +### 6.4 Reverse Order Cancellation API +**HTTP Method**: `POST` +**Endpoint Path**: `/v2/clients/requests/mark_cancel` +**Description**: Cancels a pending customer pickup request before rider execution. + +#### Sample Request Payload +```json +{ + "client_request_id": "REV-ORD-2026-8801", + "reason": "Customer withdrew return request" +} +``` + +### 6.5 Single Pickup Request Tracking API (V4) +**HTTP Method**: `GET` +**Endpoint Path**: `/v4/clients/requests/{client_request_id}` +**Description**: Retrieves tracking timeline, rider contact, QC status, and location for a reverse pickup. + +#### Sample Response Payload (`200 OK`) +```json +{ + "status": "success", + "data": { + "client_request_id": "REV-ORD-2026-8801", + "awb_number": "SFX-REV-99182736", + "current_status": "picked_up", + "qc_status": "PASSED", + "rider_details": { + "name": "Ramesh Kumar", + "phone": "+919876543210" + }, + "tracking_history": [ + { + "status": "new", + "timestamp": "2026-09-25T15:00:00Z", + "remarks": "Reverse pickup requested" + }, + { + "status": "pickup_assigned", + "timestamp": "2026-09-25T15:15:00Z", + "remarks": "Assigned to rider Ramesh" + }, + { + "status": "picked_up", + "timestamp": "2026-09-25T16:00:00Z", + "remarks": "Item passed QC check and collected from customer" + } + ] + } +} +``` + +### 6.6 Bulk Pickup Request Tracking API (V4) +**HTTP Method**: `POST` +**Endpoint Path**: `/v4/clients/requests/bulk_query` +**Description**: Batch queries status for up to 50 reverse pickup requests. + +#### Sample Request Payload +```json +{ + "client_request_ids": [ + "REV-ORD-2026-8801", + "REV-ORD-2026-8802" + ] +} +``` + +### 6.7 Update QC Flag API +**HTTP Method**: `PUT` +**Endpoint Path**: `/v2/clients/requests/update_qc/` +**Description**: Updates QC verification flags or serial number requirements prior to pickup attempt. + +#### Sample Request Payload +```json +{ + "client_request_id": "REV-ORD-2026-8801", + "qc_check": true, + "serial_number": "SN-9988112244" +} +``` + +### 6.8 Proof of Pickup / POD API +**HTTP Method**: `GET` +**Endpoint Path**: `/v1/clients/pod_details/?client_request_id={client_request_id}` +**Description**: Retrieves digital signature image URL and customer OTP verification status for completed pickups. + +#### Sample Response Payload (`200 OK`) +```json +{ + "status": "success", + "data": { + "client_request_id": "REV-ORD-2026-8801", + "pickup_timestamp": "2026-09-25T16:00:00Z", + "signature_url": "https://cdn.shadowfax.in/pop/signatures/REV-ORD-2026-8801.png", + "qc_photos": [ + "https://cdn.shadowfax.in/qc/photos/REV-ORD-2026-8801_1.jpg" + ], + "otp_verified": true + } +} +``` + +--- + +## 7. Appendix: Standard Reverse Remark & QC Fail Codes Catalog + +Shadowfax returns standardized remark and reason codes during reverse pickup exceptions. + +### 7.1 Quality Check (QC) Failure Reason Codes +| Reason Code | Description | Customer & System Handling | +| :--- | :--- | :--- | +| `QC_FAIL_BRAND_MISMATCH` | Item brand does not match order manifest. | Re-check customer item or reject return request. | +| `QC_FAIL_SERIAL_MISMATCH` | Serial/IMEI number on item differs from manifest. | Verify original order invoice serial number. | +| `QC_FAIL_DAMAGED_ITEM` | Item exhibits physical damage not recorded in return request. | Notify merchant customer support. | +| `QC_FAIL_MISSING_ACC` | Accessories/chargers missing from return package. | Inform customer to provide complete box contents. | +| `QC_FAIL_USED_ITEM` | Item shows clear signs of usage (tags removed). | Reject return per merchant policy. | + +### 7.2 Customer Delay & Intention (CID) Codes +| Reason Code | Description | Action | +| :--- | :--- | :--- | +| `CID_CUST_UNAVAILABLE` | Customer not available at pickup address. | Reschedule for next business day. | +| `CID_ITEM_NOT_PACKED` | Customer item not packed or ready for handoff. | Reschedule pickup window. | +| `CID_CUST_REFUSED` | Customer changed mind and refused to hand over item. | Trigger cancellation workflow. | + +### 7.3 On-Hold & Unreachable Remark Codes +| Reason Code | Description | Action | +| :--- | :--- | :--- | +| `NC_PHONE_UNREACHABLE` | Rider unable to reach customer phone. | Trigger automated SMS/WhatsApp call-back link. | +| `NC_ADDRESS_INCOMPLETE`| Customer address untraceable by rider. | Request customer address update via client UI. | + +--- + +## 8. Integration Walkthrough & Next Steps + +1. **API Token Setup**: Request Reverse API Token from Shadowfax Integration Team. +2. **Webhook Registration**: Configure HTTPS endpoint for receiving reverse state callbacks (`picked_up`, `qc_failed`, `delivered_to_warehouse`). +3. **Sandbox Testing**: Create test pickup request $\rightarrow$ Execute simulated QC pass/fail $\rightarrow$ Receive Webhooks $\rightarrow$ Retrieve POD. +4. **Go-Live Readiness**: Transition endpoint URL to Production host `https://flash.shadowfax.in`. \ No newline at end of file diff --git a/documentation/sfx_reverse_pickup_spec.apib b/documentation/sfx_reverse_pickup_spec.apib new file mode 100644 index 0000000..850602b --- /dev/null +++ b/documentation/sfx_reverse_pickup_spec.apib @@ -0,0 +1,1894 @@ + +FORMAT: 1A +HOST: https://dale.staging.shadowfax.in/api + +# Shadowfax Reverse Pickup API + +Using the Shadowfax Reverse Pickup API collection, developers can integrate our customer pickup platform into their applications. The API is designed to allow application developers to check Pincode serviceability, place and manage orders and then track order status till completion. + +*Use this API if the use case is to return the picked item to your warehouse. If you want us to return the picked item +to the individual seller, please refer to [Reverse Pickup - Seller Delivery document](https://sfxreversepickupsellerdelivery.docs.apiary.io/).* + + +## Glossary + +###DCs and Hubs +*DC*: Large facilities typically located in the outskirts of a city where all shipments(intercity and intracity both) are processed.
+ + +*Hub*: Small facilities located across the city from where last-mile delivery and first mile pick up happens. + + +####Following terms are defined with respect to a shipment's location +* Origin Hub:
+This is the first shadowfax facility where a packet reaches directly from seller or customer +* Origin DC:
+This is the first DC where a packet is sent from the client Warehouse or origin hub to process further. +* Destination Hub:
+This is the last shadowfax facility where a packet reaches before delivering to customer or seller +* Destination DC:
+This is last DC where a packet is sent before sending to destination hub or client Warehouse +* Via DC:
+All DCs other than origin and destination where a shipment has to go through. Manifest bags are not opened at these DCs and will be directly passed on to the next location. + +####Type of Operations +* Forward Operations:
+Orders that are originating at client warehouse or seller side and are to be delivered to end customer.
+
+* Reverse Operations:
+Return orders that are originating at customer side or forward orders that could not be delivered to the customer and are to be returned to client warehouse or seller.
+ +####Miscellaneous +
+* AWBs:
+Unique Identification Number for each item/packet
+* LOR:
+Letter of Receipt, a document for acknowledging the delivery or receipt of shipments/ packets
+* POD(Proof of Delivery):
+A receipt of pickup and delivery of items + + + +##Authentication +Shadowfax API uses Token Authentication. You will be shared Staging and Production API keys from the Shadowfax Team via an automated email. +

The `Authorization` header that should be used while sending request will look like: +
+`Token e43145811ca6e75cde2d232df3d41485` where `e43145811ca6e75cde2d232df3d41485` is the token shared. +
The `Authorization` value itself contains the Word “Token”. + +##Requests +This is base URL.Please refer to endpoints to make actual request.The base URL for all requests to the Production Shadowfax Server is given below:
+||| +|------------|-----------| +|Staging|`https://dale.staging.shadowfax.in/api`| +|Production|`https://dale.shadowfax.in/api`| + +All our API is REST-based. This means: +* It makes use of standard HTTP verbs like GET, POST, DELETE. +* The API uses standard HTTP error responses to describe errors. +* Authentication is specified with HTTP Token Authentication. + +POST data should be encoded as standard `application/json` +|Accepted Format|| +|--------------------|--------------------| +|Date|(YYYY:MM:DD)| +|DateTime |(YYYY:MM:DD HH:MM:SS)| + +##Responses +Shadowfax API uses HTTP status codes to indicate the status of your requests. This includes successful and unsuccessful responses. + +* All responses are in [JSON]('http://www.json.org'). + +* All response Timestamps follow [ISO-8061]('https://www.w3.org/TR/NOTE-datetime') format. + > Times are expressed in local time, together with a time zone offset in hours and minutes. A time zone offset of "+hh:mm" indicates that the date/time uses a local time zone which is "hh" hours and "mm" minutes ahead of UTC. A time zone offset of "-hh:mm" indicates that the date/time uses a local time zone which is "hh" hours and "mm" minutes behind UTC. + +#### Status Codes + +* 200 - OK: Everything went as planned. +* 400 - Bad Request: You did something wrong. Often a missing argument or parameter. +* 401 - Unauthorized: Authentication was incorrect. +* 404 - Not Found +* 500 - Internal Server Error: We had a problem processing the request. +* 503 - Service Unavailable: Try again later. + +##Order States +A given pickup order can be in one of the following states. This state will be shared in the `status` field in the Order Tracking APIs. + +|Order State |comment |Description | +|-------------------------------|---------------------------------------------------------|--------------------------------| +| `New` | Item New at ` ` | When the pickup request is received in the Shadowfax system | +| `Assigned` | Item Assigned For Customer Pickup at `` | When the pickup request is assigned to a pickup executive for pickup | +| `Out For Pickup` | Item Out For Pickup at `` | When the assigned pickup executive is Out for Pickup | +| `Picked` | Picked Successfully | When the item is picked by the pickup executive | +| `Received` | Received at hub Successfully | When the picked item is received at the hub | +| `Received at Return DC` | Received at Return DC Successfully | When the picked item is received at the Return DC | +| `Cancelled` | Refer [Appendix](#Appendix_cancelled) for detailed reasons of order Cancelled.| When the pickup request is cancelled | +| `Cid` | Refer [Appendix](#Appendix_cid) for detailed reasons of Cid | When the pickup is delayed by the customer to another date | +| `Not Contactable` | Refer [Appendix](#Appendix_nc) for detailed reasons of Not Contactable | When the customer is not contactable by the delivery executive | +| `Not Attempted` | Refer [Appendix](#Appendix_na) for detailed reasons of Not Attempted | When the assigned pickup executive couldn't attempt the pickup | +| `Returned To Client` | Item Return To Origin Delivered at `` | When the item is returned to the seller destination pincode hub | +| `Undelivered` | Item Return to Origin Not Delivered at `` | When there is a failed delivery to client warehouse or seller address | +| `Lost` | Item Lost at `` | When the picked item is Lost in transit or at hub | +| `QC Failed` | Refer [Appendix](#Appendix_qc) for detailed reasons of QC Failed | When doorstep QC is failed for the pickup item | +| `On Hold` | Refer [Appendix](#Appendix_On_Hold) for detailed reasons of On Hold | When the item is put On Hold on client orders or due to Operational Concerns | +| `Item added to Bag` | Item added to bag at `` | When the order is added to the bag | +| `Bag In Transit` | Bag in transit from `` | This status signifies that the bag (Master Manifest) is in transit. | +| `Bag Received` | Bag received at `` | This status signifies that the bag (Master Manifest) has been received at the destination facility | +| `Received` | Item Return To Origin at `` | The Item Returned To Origin | +| `Item Misrouted` | Item misrouted at `` | The shipment reached to a wrong sfx facility | +| `Pincode Updated` | Item destination pincode updated at `` | The Item destination pincode updated | +| `In Transit for Return` | The Bag in transit return from `` | This status signifies that the bag (Master Manifest) is in transit during rts journey.| +| `Bag in transit for return` | Bag in transit for return at ``| The Bag in transit for return at sfx facility | + + +##Push Callback API +We have built a generic PUSH API callback for the Reverse model. The client can enable it to get real-time notification of each push notification status change. +### Callback Header details + +The Standard callback header has below keys: + ++ Headers + + Authorization: Token c6b780ae9250f3 + Content-Type: application/json + +Here we have the capability to add/modify the hearder keys as shown in the example below: + ++ Headers + + Content-Type: application/json + Authorization: d5eecf30ae6eb8d158dc54c77178ab9 + account_name: SF_ACC_STAGING + + +### Request Body details + +The callback contains the below parameters: + +* `awb_number` - Generated by Shadowfax while placing an order. +* `order_id` - client_order_id shared while placing an order. +* `event_timestamp` - event_timestamp of the current status. +* `current_location` - The current location of the order during the event. +* `comments` - Contains the remarks associated with the status. +* `event` - Current status id of the shipment +* `status` - Current status of the shipment +* `otp_verifed` - The value indicates whether the status was marked after OTP verification. The possible values are `"Y"/"N"/"NA"`. By Default `"NA"`. +* `rider_name` - The name of the rider delivering the order.The rider details are shared when the current status is Out For Pickup (ofp). +* `rider_contact` - The contact details of the rider delivering the order.The rider details are shared when the current status is Out For Pickup (ofp). +* `type` - The order type is shared over this field. eg: FWD/REV +* `recepient_info` - This field contains proof of delivery, and it will be sent only when the current status has been delivered or RTS_D has been confirmed. +* `qc_images` - This field contains the images taken during QC failed. + +This is the structure of the payload data(JSON) that will be pushed to your end. + ++ Response 200 (application/json) + + { + "awb_number": "R18481863TMA", + "order_id": "testwebhookrev10", + "event_timestamp": "2022-06-17 16:54:49", + "current_location": "DEL_Trinagar", + "comments": "Item Assigned For Customer Pickup at DEL_Trinagar", + "event": "assigned_for_pickup", + "status": "Assigned", + "otp_verifed" : "NA", + "rider_name": null, + "rider_contact": null, + "client_id": 1707, + "recipient_info":[], + "qc_images": [], + "type": "REV" + } + +When we want to send a push message to your server, we need to make an API call to a push service. API call would require the following callback details, which can be configured via the Client Portal on the Webhook tab. + +`The appropriate URL implemented at your end to trigger a push message(Mandatory).` + +`Authentication Key(Non mandatory).` + +|Order State |comment |Description | +|-------------------------------|---------------------------------------------------------|--------------------------------| +| `Assigned` | Item Assigned For Customer Pickup at `` | When the pickup request is received in the Shadowfax system | +| `Out For Pickup` | Item Out For Pickup at `` | When the assigned pickup executive is Out for Pickup | +| `Picked` | Picked Successfully | When the item is picked by the pickup executive | +| `Received at Hub` |Received at hub Successfully | When the picked item is received at the hub | +| `Received at Return DC` | Item Received at RTS DC at `` | When the picked item is received at the Return DC | +| `Cancelled` | Refer [Appendix](#Appendix_cancelledpushapiwarehouse) for detailed reasons of order Cancelled.| When the pickup request is cancelled | +| `Cid` | Refer [Appendix](#Appendix_cidpushapiwarehouse) for detailed reasons of Cid | When the pickup is delayed by the customer to another date | +| `Not Contactable` | Refer [Appendix](#Appendix_ncpushapiwarehouse) for detailed reasons of Not Contactable | When the customer is not contactable by the delivery executive | +| `Not Attempted` | Refer [Appendix](#Appendix_napushapiwarehouse) for detailed reasons of Not able to attempt | When the assigned pickup executive couldn't attempt the pickup | +| `Returned To Client` | Item Return To Seller Delivered at `` | When the item is returned to the seller destination pincode hub | +| `Undelivered` | Item Return to Seller Not Delivered at `` | When there is a failed delivery to client warehouse or seller address | +| `Lost` | Item Lost at `` | When the picked item is Lost in transit or at hub | +| `QC Failed` | | When doorstep QC is failed for the pickup item | +| `On Hold` | Refer [Appendix](#Appendix_On_Holdpushapiwarehouse) for detailed reasons of On Hold | When the item is put On Hold on client orders or due to Operational Concerns. | +| `Returned To Client` | Item Return To Origin Delivered at `` | The Item Return To Origin Delivered | +| `Undelivered` | Item Return to Origin Not Delivered at `` | The Item Return to Origin Not Delivered | +| `ReOpen` | Product is reopened | The product reopend for delivery | + + +##QC Process +We follow the app-based doorstep QC process for all the eligible shipments picked up by our delivery executives. +Rules for the QC process are currently based on the *Return Reason* shared by the client. These rules can be configured in our internal Operations portal, for which details can be taken from the Sales POC. + +In Order Tracking APIs, please look for the values of the following fields in the SKU details - + +* `picked` - If the item is picked, it's value will be `picked`, otherwise it'll be `not_picked` +* `qc_status` - If the item has passed all QC checks, it's value will be `passed`. If it has failed a check, it's value will be `fail`. +In some cases, where we do Secondary QC at hub and the item got failed due to Secondary QC, then it's value will be `fail_hub` +* `qc_remarks` - If the item has failed QC checks, then the reasons for failure are shared in this field + +eg: In case of QC failed status: + +`"picked": "not_picked"` + +`"qc_status": "fail"` + +`"qc_remarks": "• Brand : No\n• Is product colour as per description ? : No\n• Category : No"` + +##Testing your Integration +In order to test your integration, you can start by placing sample order requests on our staging server. +We've created an Order Status Update API as well which works only in the staging environment and it'll allow you to update the order status. +You can use this API to see if the Pull Tracking APIs are working fine or not. + + +# Group Serviceability + +## Unified Pincode Serviceability [/v1/clients/serviceability/?service={service}&page={page}&count={count}&pincodes={pincodes}] +### List All Serviceable Pin Codes [GET] + +By using this endpoint, you can get a list of all Shadowfax serviceable pincodes, as well as the services provided by each of them. + +If pagination is not sent as a parameter then by default 25 pin codes will be retrieved. + +While triggering the API enter the service for which the list of pincode needs to be fetched. +* The service parameter can be: + * `seller_pickup` - List all the pincodes where seller pickup is available. + * `customer_delivery` - List all the pincodes where customer delivery is available. + * `customer_pickup` - List all the pincodes where customer pickup is available. + * `seller_delivery` - List all the pincodes where seller delivery is available. + * `warehouse_pickup` - List all the pincodes where warehouse pickup is available. + * `warehouse_return` - List all the pincodes where warehouse return or rto is available. + + +##Requests +This is the URL to make an actual request:
+||| +|------------|-----------| +|Staging|`https://dale.staging.shadowfax.in/api/v1/clients/serviceability/?service=customer_pickup&page=1&count=10&pincodes=560016,560017`| +|Production|`https://dale.shadowfax.in/api/v1/clients/serviceability/?service=customer_pickup&page=1&count=10&pincodes=560016,560017`| + + ++ Parameters + + + service:customer_pickup (required, string) - Enter the service for which the list of pincodes needs to be fetched. + + page:1 (number)- Enter the page number. + + count:10 (number) - Enter the per page count. + + pincodes:560017 (number) - Enter the pincodes. + + ++ Request (application/json) + + + Headers + + Authorization: Token + ++ Response 200 (application/json) + + [ + { + "code": 560008, + "services": [ + "Regular", + "Surface" + ] + }, + { + "code": 560016, + "services": [ + "Regular" + ] + }, + { + "code": 560038, + "services": [ + "Regular", + "Surface" + ] + }, + { + "code": 560047, + "services": [ + "Regular" + ] + }, + { + "code": 560048, + "services": [ + "Regular", + "Surface" + ] + }, + { + "code": 560067, + "services": [ + "Regular", + "Surface" + ] + }, + { + "code": 560071, + "services": [ + "Regular", + "Surface" + ] + }, + { + "code": 560098, + "services": [ + "Regular", + "Surface" + ] + }, + { + "code": 500484, + "services": [ + "Regular", + "Surface" + ] + }, + { + "code": 560005, + "services": [ + "Regular", + "Surface" + ] + } + ] + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 400 (application/json) + + { + "message": "Invalid Request" + } + +# group Reverse AWB Generation +## AWB Generation [/v3/clients/orders/generate_awb/] + +### Generate AWB [POST] +We have an automated process of sharing new AWBs once the existing ones are about to exhaust. This endpoint is used to generate the AWB numbers. It takes count of AWB Number as parameters. +For any changes, please reach out to Shadowfax's Account Manager. +||| +|------------|-----------| +|Staging|`https://dale.staging.shadowfax.in/api/v3/clients/orders/generate_awb/`| +|Production|`https://dale.shadowfax.in/api/v3/clients/orders/generate_awb/`| + ++ Request + + + Body + + { + "count" : 10 + } + + + Headers + + Authorization: Token + ++ Response 200 (application/json) + + { + "message": "success", + "awb_numbers": [ + "R82064314", + "R76634196", + "R72416059", + "R71234141", + "R86383023", + "R67263848", + "R59587413", + "R84059950", + "R89622149", + "R49323697" + ] + } ++ Response 400 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + +# group Order Creation +## Order Creation [/v3/clients/requests] +### Create a Regular Pickup Request[POST] +This endpoint is used to create a new pickup request in our reverse pickup system. +It takes pickup customer specific details, pickup destination details and product attributes details. + +Below given are sample pincodes for testing in Staging Environment +* 110009 +* 110001 +* 560007 + ++ Request (application/json) + + + Body + + { + "client_order_number": "0121001", + "warehouse_name": "Client Warehouse", + "warehouse_address": "Client Warehouse Address", + "destination_pincode": 500001, + "unique_code": "warehouse01", + "total_amount": 430, + "price": 412, + "eway_bill": 123456789012, + "pickup_type": "regular", + "address_attributes": { + "address_line": "ho no-68,bhati mkt,main dadri road,surajpur", + "city": "Noida", + "country": "India", + "pincode": 201301, + "name": "Saurabh Shukla", + "phone_number": "9540914499", + "alternate_contact": "9999999999", + "sms_contact": "9999999999", + "latitude" : "12.00000" , + "longitude" : "77.00000", + "location_accuracy" : "L" , + "location_type" : "residential" + }, + "weight_details": { + "actual_weight": 2.01, + "volumetric_weight": 5.5 + }, + "skus_attributes": [{ + "name": "Puma-Men-Black-Wirko-Xc-Casual-Shoes (Size: 8)", + "client_sku_id": "SC45634", + "price": 100, + "brand": "Puma", + "category": "Shoes", + "return_reason": "Size Mismatch", + "qc_required": "false", + "qc_rules": [{ + "question": "Is product colour as per description ?", + "is_mandatory": 1, + "value": "Red" + }], + "seller_details": { + "regd_name": "Seller Name", + "regd_address": "GST Regd address", + "state": "Karnataka", + "gstin": "36AAVCS6697K1Z4" + }, + "taxes": { + "cgst_amount": 0.0, + "sgst_amount": 0.0, + "igst_amount": 18.0, + "total_tax_amount": 18.0 + }, + "hsn_code": "83294089", + "invoice_id": "ADSf", + "additional_details": { + "color": "black", + "size": 8, + "sku_images": ["https://sku_image_url/1/","https://sku_image_url/1/"], + "quantity_value" : 10, + "quantity_unit": "EA" + } + }] + } + + + Attributes + + Required Parameters (required, object) + + client_order_number (required, string) - Order number in client's system. Character limit : 100 + + warehouse_address (required, string) - Client warehouse address to which the item will be returned + + destination_pincode (required, string) - 6 digit valid Pincode. This is the pincode where the picked up product needs to be sent to + + unique_code (optional, string) - 255 (max length) a unique warehouse code. + + pickup_type (required, string) - Order Pickup Type. Options: regular, slot and surface + + total_amount (optional, number) - Total amount (inclusive GST) of the order + + price (required, number) - Price (exclusive of GST) of the order + + eway_bill (required, number) - 12 digit E-way bill. If the total amount is greater than 50,000 then this field is mandatory, otherwise optional. Character limit : 12 + + address_attributes (required, object) - This is the pickup details. + + address_line(required, string)- Pickup Address + + city(required, string) - Pickup City.Character limit : 50 + + pincode(required, number) - 6 digit valid pincode of Pickup Address + + name (required, string) - Pickup Customer's Name. Character limit : 100 + + phone_number (required, string) - Pickup Customer's Contact Number.Character limit :13 + + alternate_contact (optional, string) - Pickup Customer's Alternate Contact Number. If provided, it should. Character limit : 13 be a minimum of 10 digits otherwise validation error will be thrown. + + sms_contact (optional, string) - Pickup Customer's Contact Number which will be used to send SMS. If provided, it should be a minimum of 10 digits otherwise validation errors will be thrown. + + latitude (optional, string) - Pickup customers latitude. + + longitude (optional, string) - Pickup customers longitude. + + location_accuracy(optional, string) - It should be H,M,L(High, Medium, Low). + + location_type(optional, string) - residential or commercial . + + weight_details (optional, object) - Weight details + + actual_weight(optional, number) - Weight of the Product (in gms) default :0.0 + + volumetric_weight(optional, number) - Volumetric Weight of the Product (in gms). default : 0.0 (volumetric weight=(l*b*h)/5000) + + skus_attributes (required, array) + + (object) + + name(required, string) - Product Name + + client_sku_id(optional, string) - Sku Id provided by you (if any). Character limit : 100 + + price (required, number) - Price of the Product + + category(optional, string) - Product Category. Character limit : 200 + + brand(optional, string) - Brand name of the Product. Character limit : 200 + + return_reason(optional,string) - Return reason for product. Character limit : 200 + + qc_required(optional, string) - Flag to determine if qc is required for the product. Default true. + + qc_rules(optional, object) - Customized QC rules for doorstep QC + + question (required, string) - Question to be displayed to the pickup boy for QC. Character limit : 255 + + is_mandatory (required, number) - If QC should fail if this check point is not passed then send 1, else send 0 + + value (optional, string) - Value of the SKU property which is asked in the question + + seller_details (required, object) - Seller details required for GST management. + + regd_name (required, string) - Registered name of the seller to which item is to be returned. Character limit : 100 + + regd_address (required, string) - Registered address of the seller + + state (required, string) - State of Registered Address of the seller. Character limit : 50 + + gstin (required, string) - 15 digit GSTIN of the seller. Character limit : 50 + + taxes (required, object) - Tax distribution according to GST + + cgst_amount (required, number) - CGST Amount + + sgst_amount (required, number) - SGST Amount + + igst_amount (required, number) - IGST Amount + + total_tax_amount (required, number) - Total GST. It is the sum of above three fields + + hsn_code (required, string) - 8 digit HSN Code of the item.Character limit : 50 + + invoice_id (required, string) - Invoice ID. Character limit : 50 + + additional_details(optional, object) - To help in better doorstep QC, kindly share the following details as well. + + color (optional, string) - Product Colour + + size (optional, string) - Product Size + + imei (optional, string) - IMEI Number of Mobile Phone (Separated by '/' in case of dual sim phones) + + sku_images (optional, array) - Array of URL of Sku Images + + serial_no (optional, string) - Serial Number of the device + + quantity_value (optional, number) - Number of items in SKU + + quantity (optional, number) - Number of items in SKU + + quantity_unit (optional, number)- Unit value of SKU's + + Optional Parameters(optional, object) + + client_request_id (optional, string) - Client Request Id. If it is not provided then we will generate it automatically. + + warehouse_name (optional, string) - Clients Warehouse Name + + + + + Headers + + Authorization: Token + + + ++ Response 200 (application/json) + + { + "message": "Success", + "client_order_number": "0121001", + "request_type": "pickup", + "client_request_id": "R10000021ZI", + "client_id": 31, + "destination_pincode": 110009, + "total_amount": 430, + "price": 412, + "eway_bill": 123456789012, + "address": { + "name": "Saurabh Shukla", + "phone_number": "9540914499", + "address_line": "ho no-68,bhati mkt,main dadri road,surajpur", + "city": "Noida", + "pincode": 201301, + "alternate_contact": "9999999999" + }, + "skus": [ + { + "client_sku_id": "SC45634", + "name": "Puma-Men-Black-Wirko-Xc-Casual-Shoes (Size: 8)", + "price": 100, + "return_reason": "Size Mismatch", + "brand": "Puma", + "category": "Shoes", + "seller_details": { + "regd_name": "Seller Name", + "regd_address": "GST Regd address", + "state": "Karnataka", + "gstin": "36AAVCS6697K1Z4" + }, + "cost": { + "cgst_amount": 0.0, + "sgst_amount": 0.0, + "igst_amount": 18.0, + "total_tax_amount": 18.0 + }, + "hsn_code": "83294089", + "invoice_id": "ADSf", + "additional_details": { + "color": "black", + "size": 8, + "sku_images": [ + "http://sku_image_url/1/", + "http://sku_image_url/1/" + ], + "quantity_value": 10, + "quantity": 10, + "quantity_unit": "EA" + } + } + ], + "status_last_updated_at": "2017-03-10T05:54:50.043Z", + "pickup_request_state_histories": null, + "status": "New", + "scheduled_date": 1489084200000, + "date_created": 1489105490000 + } + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Order data can not be empty." + } + ++ Response 400 (application/json) + + {"message": "Your account is not activated yet. Our sales team would reach out to you soon for completing your onboarding process."} + + ++ Response 400 (application/json) + + { + "message": "awb_number: [This field must be unique]" + } + ++ Response 400 (application/json) + + { + "COID": "1234", + "message": "Failure", + "errors": "Order for COID : 1234 is already created with AWB : R17806145san", + "AWB": "R17806145san" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Client Order Number is Required" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Address Attributes are Required" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Customer Pincode is Required" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Invalid date format for invoice date. Please use yyyy-MM-dd" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Invalid Customer contact number" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Customer phone_number can not be blank" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Return Details are required" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Please Enter E-Way Bill Number", + "data": {}, + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Failure", + "message": "Failure", + "errors": "Please Enter Valid 12-digit E-Way Bill Number", + "data": {}, + "responseCode": 400 + } + ++ Response 500 (application/json) + + { + "message": "Internal Server Error" + } + +# group Order Cancellation +## Order Cancellation [/v2/clients/requests/mark_cancel] + +### Cancel a Pickup Request [POST] + +This endpoint is used to cancel a pickup request. It takes AWB Number or unique client order id of the order along with the cancellation reason as parameters. + +* A pickup order can be cancelled before if its status is **NOT** in `Out for Pickup`, `Cancelled` and `Picked` + +If you trigger the cancellation API during `Out for Pickup`(ofp) status, API response would return the `responseMsg` & `responseCode` as `Pickup request cannot be cancelled now` & `400` respectively. + +If you trigger the cancellation API during `Picked` status, API response would return the `responseMsg` & `responseCode` as `Pickup request cannot be cancelled now` & `400` respectively. + +If you trigger the cancellation API during `Cancelled` status, API response would return the `responseMsg` & `responseCode` as `Pickup request cannot be cancelled now` & `400` respectively. + +* Following are the valid Cancellation Remarks. These are entered in the `cancel_remarks` field. + * Cancelled By Customer + * Incorrect/ Incomplete contact info + * Payment Issue + ++ Request + + + Body + + { + "request_id": "SFXE220250035SK", + "cancel_remarks ": "Request cancelled by customer" + } + + + Headers + + Authorization: Token + + + Attributes + + + Required Parameters(required, object) + + request_id(required, string) - AWB number of the order or the client order id of the order. There should be a unique client order id for each order. + + cancel_remarks(required , string) - Valid options are discussed in the description + ++ Response 200 (application/json) + + { + "responseMsg": "Request has been marked as cancelled", + "responseCode": 200 + } + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + + ++ Response 200 (application/json) + { + "responseMsg": "Invalid Token.", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "message" : "Invalid AWB Number" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Multiple Orders found for the given Request Id", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Cannot cancel qc order from Out For Pickup", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Cannot cancel order from Picked", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Cannot cancel order from Received", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Pickup request cannot be cancelled now", + "responseCode": 400 + } ++ Response 400 (application/json) + + { + "responseMsg": "request_id and cancel_remarks are required", + "responseCode": 400 + } + ++ Response 200 (application/json) + + { + "responseMsg": "Order not mapped with the client.", + "responseCode": 400 + } + + +#group Escalation API +## Escalation API [/v1/clients/support/issue/] +### Escalation API [POST] +This endpoint to take escalations from clients on real time. Based on the issue category, a ticket would be raised in the system. This API is to take the escalation input from the clients. The action being taken on those tickets . + +####Issue Category options + +|Issue Category |Issue Category Code |Validations | Order Type | +|--------------------------------------|---------------------|----------------------------------------------------------------------------------------------------------------|---------------------| +| `Delayed Delivery` | 1 |To be passed post an order is successfully picked in system and not delivered post to Estimated Delivery Date | Forward | +| `Expedite Pickup - Customer` | 2 |To be passed only if the order is not yet picked from the Customer | Reverse | +| `Expedite Pickup - Seller` | 3 |To be passed only until the seller pickup is not completed | Forward | +| `Status Mismatch` | 4 |To be passed when there is a mismatch in expected and received order status | Forward/Reverse | +| `Delivery Dispute` | 5 |To be passed only post the order is marked Delivered in the system | Forward | + + ++ Request + + Body + { + "awb_number" : "SF00000000TC", + "issue_category" : 1 + } + + Headers + Authorization: Token Authorization + + Attributes + + Required Parameters(required, object) + + awb_number(required, string) - It should be a valid Awb number. + + issue_category(required , number) - Issue category will be one among choices given in Issue Category options . + ++ Response 200 (application/json) + + { + "message": "Issue received", + "status": "SUCCESS" + } + + +# Group Order Details + +## Single Pickup Request v4 Version [/v4/clients/requests/{client_request_id}] + ++ Parameters + + client_request_id (required, string) ... AWB Number of the order + +### Get Detail of a Pickup Request[GET] + +This endpoint is used to get all details of a pickup request defined by its client request id.The v4 version of tracking API provides the additional statuses of the reverse flow like Received at Return DC,Undelivered. + ++ Request + + + Header + + Authorization: Token + ++ Response 200 (application/json) + + { + "client_order_number": "SKU12345", + "request_type": "pickup", + "client_request_id": "R11507439TST", + "client_id": 18, + "destination_pincode": 999998, + "price": 6000.0, + "address": { + "name": "rtu", + "phone_number": null, + "address_line": "No 849 Binnamangala, Indiranagar 100 Feet Road", + "city": "Bangalore", + "state": "Karnataka", + "pincode": 999999, + "created_at": "2022-02-28T10:26:19.000000+0000", + "updated_at": "2022-02-28T10:26:19.000000+0000", + "alternate_contact": "9999999999" + }, + "skus": [ + { + "client_sku_id": "1234", + "name": "Hawes and Curtis Light Blue Solid Shirt", + "price": 0.0, + "return_reason": "Looks Different, Size Mismatch", + "brand": "Brand1", + "category": "ShirT", + "additional_details": { + "colour": "Light Blue", + "size": "S1", + "quantity": "4", + "sku_images": [ + "2342222222222220066816" + ] + }, + "picked": "picked", + "qc_status": "passed", + "qc_remarks": "", + "seller_details": { + "regd_name": "ABC", + "regd_address": "Something Something", + "state": "Karnataka", + "gstin": "098324" + }, + "taxes": { + "cgst_amount": 12.0, + "sgst_amount": 12.0, + "igst_amount": 0.0, + "total_tax_amount": 24.0 + }, + "hsn_code": "2343413", + "invoice_no": "1", + "qc_images": [] + }, + { + "client_sku_id": "1237", + "name": "my item9", + "price": 0.0, + "return_reason": null, + "brand": "Brand2", + "category": "Dress", + "additional_details": { + "colour": " Pink", + "size": " S2" + }, + "picked": "picked", + "qc_status": "passed", + "qc_remarks": "", + "seller_details": { + "regd_name": "BAC", + "regd_address": "Something", + "state": "Karnataka", + "gstin": "3289437" + }, + "taxes": { + "cgst_amount": 0.0, + "sgst_amount": 0.0, + "igst_amount": 12.0, + "total_tax_amount": 12.0 + }, + "hsn_code": "42536", + "invoice_no": "2", + "qc_images": [] + }, + { + "client_sku_id": "1236", + "name": "item3", + "price": 0.0, + "return_reason": null, + "brand": null, + "category": null, + "additional_details": {}, + "picked": "picked", + "qc_status": "passed", + "qc_remarks": "", + "seller_details": { + "regd_name": null, + "regd_address": "", + "state": null, + "gstin": null + }, + "taxes": { + "cgst_amount": null, + "sgst_amount": null, + "igst_amount": null, + "total_tax_amount": null + }, + "hsn_code": null, + "invoice_no": null, + "qc_images": [] + }, + { + "client_sku_id": "1232", + "name": "item4", + "price": 0.0, + "return_reason": null, + "brand": null, + "category": null, + "additional_details": {}, + "picked": "picked", + "qc_status": "passed", + "qc_remarks": "", + "seller_details": { + "regd_name": null, + "regd_address": "", + "state": null, + "gstin": null + }, + "taxes": { + "cgst_amount": null, + "sgst_amount": null, + "igst_amount": null, + "total_tax_amount": null + }, + "hsn_code": null, + "invoice_no": null, + "qc_images": [] + } + ], + "seller": null, + "status_last_updated_at": "2022-02-28T10:42:47Z", + "pickup_request_state_histories": [ + { + "created_at": "2022-02-28T10:26:19Z", + "current_location": "SFX TestHub", + "state": "New", + "comment": "Item New at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:26:50Z", + "current_location": "SFX TestHub", + "state": "Assigned", + "comment": "Item Assigned For Customer Pickup at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:27:00Z", + "current_location": "SFX TestHub", + "state": "Out For Pickup", + "comment": "Item Out For Pickup at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:27:28Z", + "current_location": "SFX TestHub", + "state": "Received", + "comment": "Item Return To Origin at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:27:28Z", + "current_location": "SFX TestHub", + "state": "Received", + "comment": "Received at hub Successfully" + }, + { + "created_at": "2022-02-28T10:29:36Z", + "current_location": "SFX TestHub", + "state": "Item added to Bag", + "comment": "Item added to bag at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:29:52Z", + "current_location": "SFX TestHub", + "state": "Bag in transit for return", + "comment": "Bag in transit for return at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:31:39Z", + "current_location": "TEST DC", + "state": "Bag Received at Via", + "comment": "Bag received at via TEST DC" + }, + { + "created_at": "2022-02-28T10:32:58Z", + "current_location": "TEST DC", + "state": "Bag in transit for return", + "comment": "Bag in transit for return at TEST DC" + }, + { + "created_at": "2022-02-28T10:33:39Z", + "current_location": "testdc3", + "state": "Bag Received", + "comment": "Bag received at testdc3" + }, + { + "created_at": "2022-02-28T10:39:07Z", + "current_location": "testdc3", + "state": "Received at Return DC", + "comment": "Item Received at dc at testdc3" + }, + { + "created_at": "2022-02-28T10:39:26Z", + "current_location": "testdc3", + "state": "Item added to Bag", + "comment": "Item added to bag at testdc3" + }, + { + "created_at": "2022-02-28T10:39:45Z", + "current_location": "testdc3", + "state": "Bag in transit for return", + "comment": "Bag in transit for return at testdc3" + }, + { + "created_at": "2022-02-28T10:40:11Z", + "current_location": "TestDC2", + "state": "Bag Received", + "comment": "Bag received at TestDC2" + }, + { + "created_at": "2022-02-28T10:40:24Z", + "current_location": "TestDC2", + "state": "Received at Return DC", + "comment": "Item Received at dc at TestDC2" + }, + { + "created_at": "2022-02-28T10:42:47Z", + "current_location": "TestDC2", + "state": "Returned To Client", + "comment": "Item Return To Origin Delivered at TestDC2" + } + ], + "status": "Returned To Client", + "scheduled_date": 1645986600000.0, + "date_created": 1646024179000.0, + "pickup_type": "regular", + "slot_start_time": null, + "slot_end_time": null + } + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 400 (application/json) + + { + "message" : "Invalid AWB Number" + } + +## Bulk Pickup Requests v4 Version [/v4/clients/requests/bulk_query] + +###Get Details of Bulk Pickup Requests [POST] + +This endpoint is used to get details of multiple pickup requests in one go. The requests are defined by their client request id. The API can be used to track up to 50 AWBs in a single API call. The v4 version of tracking API provides the additional statuses of the reverse flow like Received at Return DC, Undelivered. + ++ Request + + + Header + + Authorization: Token Authorization + + + Body + + { + "request_ids": ["R11507439TST"] + } + + + Attributes + + + request_ids (required, array) - Array of AWB numbers for which details are required + + ++ Response 200 (application/json) + +{ + "R11507439TST": [ + { + "client_order_number": "SKU12345", + "request_type": "pickup", + "client_request_id": "R11507439TST", + "client_id": 18, + "destination_pincode": 999998, + "price": 6000.0, + "address": { + "name": "rtu", + "phone_number": null, + "address_line": "No 849 Binnamangala, Indiranagar 100 Feet Road", + "city": "Bangalore", + "state": "Karnataka", + "pincode": 999999, + "created_at": "2022-02-28T10:26:19.000000+0000", + "updated_at": "2022-02-28T10:26:19.000000+0000", + "alternate_contact": "9999999999" + }, + "skus": [ + { + "client_sku_id": "1234", + "name": "Hawes and Curtis Light Blue Solid Shirt", + "price": 0.0, + "return_reason": "Looks Different, Size Mismatch", + "brand": "Brand1", + "category": "ShirT", + "additional_details": { + "colour": "Light Blue", + "size": "S1", + "quantity": "4", + "sku_images": [ + "2342222222222220066816" + ] + }, + "picked": "picked", + "qc_status": "passed", + "qc_remarks": "", + "seller_details": { + "regd_name": "ABC", + "regd_address": "Something Something", + "state": "Karnataka", + "gstin": "098324" + }, + "taxes": { + "cgst_amount": 12.0, + "sgst_amount": 12.0, + "igst_amount": 0.0, + "total_tax_amount": 24.0 + }, + "hsn_code": "2343413", + "invoice_no": "1", + "qc_images": [] + }, + { + "client_sku_id": "1237", + "name": "my item9", + "price": 0.0, + "return_reason": null, + "brand": "Brand2", + "category": "Dress", + "additional_details": { + "colour": " Pink", + "size": " S2" + }, + "picked": "picked", + "qc_status": "passed", + "qc_remarks": "", + "seller_details": { + "regd_name": "BAC", + "regd_address": "Something", + "state": "Karnataka", + "gstin": "3289437" + }, + "taxes": { + "cgst_amount": 0.0, + "sgst_amount": 0.0, + "igst_amount": 12.0, + "total_tax_amount": 12.0 + }, + "hsn_code": "42536", + "invoice_no": "2", + "qc_images": [] + }, + { + "client_sku_id": "1236", + "name": "item3", + "price": 0.0, + "return_reason": null, + "brand": null, + "category": null, + "additional_details": {}, + "picked": "picked", + "qc_status": "passed", + "qc_remarks": "", + "seller_details": { + "regd_name": null, + "regd_address": "", + "state": null, + "gstin": null + }, + "taxes": { + "cgst_amount": null, + "sgst_amount": null, + "igst_amount": null, + "total_tax_amount": null + }, + "hsn_code": null, + "invoice_no": null, + "qc_images": [] + }, + { + "client_sku_id": "1232", + "name": "item4", + "price": 0.0, + "return_reason": null, + "brand": null, + "category": null, + "additional_details": {}, + "picked": "picked", + "qc_status": "passed", + "qc_remarks": "", + "seller_details": { + "regd_name": null, + "regd_address": "", + "state": null, + "gstin": null + }, + "taxes": { + "cgst_amount": null, + "sgst_amount": null, + "igst_amount": null, + "total_tax_amount": null + }, + "hsn_code": null, + "invoice_no": null, + "qc_images": [] + } + ], + "seller": null, + "status_last_updated_at": "2022-02-28T10:42:47Z", + "pickup_request_state_histories": [ + { + "created_at": "2022-02-28T10:26:19Z", + "current_location": "SFX TestHub", + "state": "New", + "comment": "Item New at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:26:50Z", + "current_location": "SFX TestHub", + "state": "Assigned", + "comment": "Item Assigned For Customer Pickup at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:27:00Z", + "current_location": "SFX TestHub", + "state": "Out For Pickup", + "comment": "Item Out For Pickup at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:27:28Z", + "current_location": "SFX TestHub", + "state": "Received", + "comment": "Item Return To Origin at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:27:28Z", + "current_location": "SFX TestHub", + "state": "Received", + "comment": "Received at hub Successfully" + }, + { + "created_at": "2022-02-28T10:29:36Z", + "current_location": "SFX TestHub", + "state": "Item added to Bag", + "comment": "Item added to bag at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:29:52Z", + "current_location": "SFX TestHub", + "state": "Bag in transit for return", + "comment": "Bag in transit for return at SFX TestHub" + }, + { + "created_at": "2022-02-28T10:31:39Z", + "current_location": "TEST DC", + "state": "Bag Received at Via", + "comment": "Bag received at via TEST DC" + }, + { + "created_at": "2022-02-28T10:32:58Z", + "current_location": "TEST DC", + "state": "Bag in transit for return", + "comment": "Bag in transit for return at TEST DC" + }, + { + "created_at": "2022-02-28T10:33:39Z", + "current_location": "testdc3", + "state": "Bag Received", + "comment": "Bag received at testdc3" + }, + { + "created_at": "2022-02-28T10:39:07Z", + "current_location": "testdc3", + "state": "Received at Return DC", + "comment": "Item Received at dc at testdc3" + }, + { + "created_at": "2022-02-28T10:39:26Z", + "current_location": "testdc3", + "state": "Item added to Bag", + "comment": "Item added to bag at testdc3" + }, + { + "created_at": "2022-02-28T10:39:45Z", + "current_location": "testdc3", + "state": "Bag in transit for return", + "comment": "Bag in transit for return at testdc3" + }, + { + "created_at": "2022-02-28T10:40:11Z", + "current_location": "TestDC2", + "state": "Bag Received", + "comment": "Bag received at TestDC2" + }, + { + "created_at": "2022-02-28T10:40:24Z", + "current_location": "TestDC2", + "state": "Received at Return DC", + "comment": "Item Received at dc at TestDC2" + }, + { + "created_at": "2022-02-28T10:42:47Z", + "current_location": "TestDC2", + "state": "Returned To Client", + "comment": "Item Return To Origin Delivered at TestDC2" + } + ], + "status": "Returned To Client", + "scheduled_date": 1645986600000.0, + "date_created": 1646024179000.0, + "pickup_type": "regular", + "slot_start_time": null, + "slot_end_time": null + } + ] +} + ++ Response 401 (application/json) + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 400 (application/json) + + { + "message" : "Invalid AWB Number" + } + ++ Response 400 (application/json) + + { + "message": "Number of AWBs exceeded. Max count allowed is 50" + } + + +#group Get POD Details + +## Get POD Details [/v1/clients/pod_details/] + +### Get POD Details[GET] + +Using this API, you can get POD (proof of delivery) details for multiple reverse delivery requests, each defined by their AWB number. +This API will allow you to get pod details for up to 100 AWB numbers. +`To obtain POD details, the order status of the AWB_number's passed must be rto_d` + ++ Request (application/json) + + + Headers + + Authorization: Token 1234567890abc1234567890xyz + + + + Body + + { + "awb_numbers": [ + "R18432490TER", + "R18443342TER" + ] + } + ++ Response 200 (application/json) + + + { + "message": "Success", + "pod_details": { + "R18432490TER": { + "recipient": null, + "recipient_contact": "None", + "recipient_signature": "[[u'https://s3-us-west-2.amazonaws.com/sfxdocs/seller/delivery_report/report_1827_1641812277.pdf']]", + "recipient_name": "None" + }, + "R18443342TER": { + "recipient": null, + "recipient_contact": "None", + "recipient_signature": "[u'https://s3.ap-south-1.amazonaws.com/sfxdocs-mumbai/delivery/pod-images/staging/seller_pod-image-50702-KBWN-1-file.jpeg']", + "recipient_name": "None" + } + } + } + ++ Response 400 (application/json) + + { + "message": "AWB does not belong to this client" + } + ++ Response 400 (application/json) + + { + "message": "Incorrect AWB" + } + ++ Response 400 (application/json) + + { + "message": "Max limit on count is 100. Please send a list within 100 AWBs" + } + ++ Response 400 (application/json) + + { + "message": "Incorrect Status" + } + ++ Response 401 (application/json) + + { + "detail": "Invalid token." + } + + + +# group Order Data Update + +## Update QC Flag [/v2/clients/requests/update_qc/] + +### Update QC flag for an AWB or SKU [PUT] + +This endpoint is used to update the QC flag for a pickup request (or individual SKU) after request creation. Users can update the qc flag either for all the SKUs or for an individual SKU. +QC flag shall not be updated once the AWB is Out of Pickup. If it returns to the hub for pickup on another day, you can then update the QC flag. + ++ Request + + + Body + + { + "awb_number": "R12688537SC", + "qc_flag": true, + "sku_id": "apple01" + } + + + Headers + + Authorization: Token + + + Attributes + + + Required Parameters(required, object) + + awb (required, string) - AWB number of the order + + qc_flag (required, string) - Boolean representing what should be the new QC required flag of the SKU + + Optional Parameters (optional, object) + + sku_id (optional, string) - Representing the SKU ID. If this is empty then all SKUs of the AWB Number is updated with the QC flag + ++ Response 200 (application/json) + + { + "responseMsg": "QC flags are updated", + "responseCode": 200 + } + ++ Response 400 (application/json) + + { + "responseMsg": "No valid SKU IDs exist for the given AWB Number", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "QC flag can't be changed now since the order is Out For Pickup", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Please specify the AWB number", + "responseCode": 400 + } + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + +## Update Order Data latest [/v1/clients/order_update/] + +### Request to Update Order Data [POST] +The endpoint allows you to edit customer details, cod amount and payment mode for an order. + +The API functionalities are as follows: + +* This API cannot be used to update the order status but since the status field is `required` it should be passed as `null`. +* The API can be used to update customer details along with the Pincode, but the Pincode can only be updated twice per order and only for intra-city orders. +* The mandatory/required attributes should be passed as `null` if they do not need to be updated. +* The Pincode cannot be updated when the current status of the order is `picked`. +* If you try to update the customer details prior to the pickup then the update would occur on pickup details i.e. the end user in case of reverse shipments. If you try to update customer details after pickup, the update would occur on drop details i.e. the seller in case of reverse shipment. + +

The `Authorization` header that should be used while sending request will look like: +
+`Token ` +
The `Authorization` value itself contains the Word “Token”. + +##Requests +This is the URL to make an actual request:
+||| +|------------|-----------| +|Staging|`https://dale.staging.shadowfax.in/api/v1/clients/order_update/ `| +|Production|`https://dale.shadowfax.in/api/v1/clients/order_update/ `| + ++ Request + + + Attributes + + + awb_numbers (required) - Awb number of an order needs to be passed. + + customer_details - Pass the customer/ delivery details + + contact - Pass customer contact. + + alternate_contact - Pass customer contact. + + customer_address - Pass customer address. + + pincode - Pass customer/delivery pincode. + + latitude - Pickup customers latitude. + + longitude - Pickup customers longitude. + + + status_update + + status - Pass `null`. + + + + Body + + { + "awb_number": "SF36901801TED", + "customer_details": { + "contact": "+919999955555", + "alternate_contact": "9999955555", + "customer_address": "#253, 2nd cross hebbal", + "pincode": 560008, + "latitude": "12.00000", + "longitude": "77.00000" + }, + + "status_update": { + "status": null + } + } + + + Headers + + Authorization: Token + Content-Type: application/json + ++ Response 200 (application/json) + + { + "message": "Request accepted." + } + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 401 (application/json) + + { + "detail": "Invalid token." + } + ++ Response 400 (application/json) + + { + "message": "Status transition from recd_at_fwd_hub to ndr is not allowed" + } + ++ Response 400 (application/json) + + { + "message": "{\"message\":\"SF36981974TED pincode has been updated 2 times. The Pincode request is stored, will be validated and updated.\"}" + } + ++ Response 400 (application/json) + + { + "message": "{\"message\":\"Intercity pincode updates are not allowed\"}" + } + ++ Response 400 (application/json) + + { + "message": "Customer details could not be updated for SF36981985TED due to invalid status. Current status is Delivered to customer" + } + ++ Response 400 (application/json) + + { + "message": "Customer contact number can not be blank" + } + + + + +# Group Issues and Bugs +Please drop in a mail to to report any bug(s) in the Client APIs. Any issue with respect to integration +can also be raised on this forum. Our team shall review the issue and get back to you in due time. + + Client Queries Troubleshooter - https://docs.google.com/spreadsheets/d/1c7EfdwrxQrqz9zhwaa4bE0dI0EI41PT5ynY74aDJ7S8/edit#gid=1815509323 + + +# Group Appendix + + +## On Hold Remarks + +Following are the remarks shared in the cases when the order is marked as `On Hold`. + +| Status | Remarks | +|---------|------------------------------------| +| On Hold | Customer want pick-up from Non-Serviceable area | +| On Hold | Pincode Address Mismatch | +| On Hold | Incorrect contact number | +| On Hold | Address issue | +| On Hold | Customer wants replacement/refund first | +| On Hold | Incomplete information received from client | +| On Hold | Mandatory check not available | +| On Hold | Pickup already done by other DSP | +| On Hold | Shipment is not picked | +| On Hold | Successful 3 attempts done | +| On Hold | Large Shipment | +| On Hold | Customer wants pickup beyond cut off time | +| On Hold | Customer changed his/her mind | +| On Hold | Doorstep QC Brandbox not available | +| On Hold | Doorstep QC Price Tag missing | +| On Hold | Doorstep QC Product Damage | +| On Hold | Doorstep QC Product Mismatch | +| On Hold | Doorstep QC Used Item | +| On Hold | Unable to pickup in given slot | +| On Hold | Non Serviceable Area | +| On Hold | Seller wants pickup beyond cut off time | +| On Hold | COVID Restricted Area | +| On Hold | Customer does not provide OTP | +| On Hold | Customer is fraud | +| On Hold | Address is junk | +| On Hold | Same City Pincode Update | +| On Hold | InterCity Shipment | + + +## Cancelled Remarks + +Following are the remarks shared in the cases when the order is marked as `cancelled`. + +| Status | Remarks | +|-----------|------------------------------------| +| Cancelled | Customer cancelled the pickup | +| Cancelled | Cancelled by Client | +| Cancelled | Seller cancelled the pickup | +| Cancelled | Customer is fraud | + +## Not Contactable Remarks + +Following are the remarks shared in the cases when the order is marked as `nc`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Not Contactable | Address issue | +| Not Contactable | Customer not reachable | +| Not Contactable | Seller not reachable | + +## QC Failed Remarks + +Following are the remarks shared in the cases when the order is marked as `qc_failed`. + +| Status | Remarks | +|-----------------|------------------------------------| +| QC Failed | QC Failed at Doorstep | + +## Cid Remarks + +Following are the remarks shared in the cases when the order is marked as `cid`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Cid | Customer Delayed Pickup | +| Cid | Seller Initiated Delay | + +## Not Attempted Remarks + +Following are the remarks shared in the cases when the order is marked as `na`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Not Attempted | Not able to attempt | +| Not Attempted | Heavy Rain | +| Not Attempted | Vehicle Breakdown | + + +## Cancelled Remarks for PUSH Api + +Following are the remarks shared in the cases when the order is marked as `cancelled`. + +| Status | Remarks | +|-----------|------------------------------------| +| Cancelled | Customer cancelled the pickup | +| Cancelled | Cancelled by Client | +| Cancelled | Seller cancelled the pickup | + + +## Cid Remarks for Push Api + +Following are the remarks shared in the cases when the order is marked as `cid`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Cid | Customer Delayed Pickup | +| Cid | Seller Initiated Delay | + + +## Not Contactable Remarks for PUSH Api + +Following are the remarks shared in the cases when the order is marked as `nc`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Not Contactable | Address issue | +| Not Contactable | Customer not reachable | +| Not Contactable | Seller not reachable | + +## Not Attempted Remarks for PUSH Api + +Following are the remarks shared in the cases when the order is marked as `Not Attempted`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Not Attempted | Not able to attempt | + + +## On Hold Remarks for PUSH Api + +Following are the remarks shared in the cases when the order is marked as `On Hold`. + +| Status | Remarks | +|---------|----------------------------------------------------| +| On Hold | Customer want pick-up from Non-Serviceable area | +| On Hold | Pincode Address Mismatch | +| On Hold | Incorrect contact number | +| On Hold | Address issue | +| On Hold | Customer wants replacement/refund first | +| On Hold | Incomplete information received from client | +| On Hold | Mandatory check not available | +| On Hold | Pickup already done by other DSP | +| On Hold | Shipment is not picked | +| On Hold | Successful 3 attempts done | +| On Hold | Large Shipment | +| On Hold | Customer wants pickup beyond cut off time | +| On Hold | Customer changed his/her mind | +| On Hold | Doorstep QC Brandbox not available | +| On Hold | Doorstep QC Price Tag missing | +| On Hold | Doorstep QC Product Damage | +| On Hold | Doorstep QC Product Mismatch | +| On Hold | Doorstep QC Used Item | +| On Hold | Unable to pickup in given slot | +| On Hold | Non Serviceable Area | +| On Hold | Seller wants pickup beyond cut off time | +| On Hold | COVID Restricted Area | \ No newline at end of file diff --git a/documentation/sfx_unified_api_spec.apib b/documentation/sfx_unified_api_spec.apib new file mode 100644 index 0000000..c6eba8c --- /dev/null +++ b/documentation/sfx_unified_api_spec.apib @@ -0,0 +1,3025 @@ +FORMAT: 1A +HOST: https://dale.staging.shadowfax.in/api/ + +# Shadowfax Unified API for Forward Integrations + +You can integrate with Shadowfax's Last Mile Customer Delivery Platform by using the Unified API Collection. Unified APIs provide a singular interface to enable Marketplace Seller and Warehouse models of operations. These APIs support end to end order lifecycle starting from Pincode serviceability to order status tracking until delivery. +## Glossary + +###DCs and Hubs +*DC*: Large facilities typically located in the outskirts of a city where all shipments(intercity and intracity both) are processed.
+ + +*Hub*: Small facilities located across the city from where last-mile delivery and first mile pick up happens. + + +####Following terms are defined concerning a shipment's location +* Origin Hub:
+This is the first Shadowfax facility where a packet reaches directly from the seller or customer +* Origin DC:
+This is the first DC where a packet is sent from the client Warehouse or origin hub to process further. +* Destination Hub:
+This is the last shadowfax facility where a packet reaches before delivering to customer or seller +* Destination DC:
+This is the last DC where a packet is sent before sending to the destination hub or client Warehouse +* Via DC:
+All DC's, other than origin and destination where a shipment has to go through. Manifest bags are not opened at these DCs and will be directly passed on to the next location. + +####Type of Operations +* Forward Operations:
+Orders that are originating at client warehouse or seller side and are to be delivered to end customer.
+
+* Reverse Operations:
+Return orders that are originating at the customer side or forward orders that could not be delivered to the customer and are to be returned to the client warehouse or seller.
+ +####Miscellaneous +
+* AWBs:
+Unique Identification Number for each item/packet
+* LOR:
+Letter of Receipt, a document for acknowledging the delivery or receipt of shipments/ packets
+* POD(Proof of Delivery):
+A receipt of pickup and delivery of items + + + +##Authentication +Shadowfax API uses Token Authentication. The Staging and Production API keys will be shared with you from the Shadowfax Team via an automated email. +

The `Authorization` header that should be used while sending the request will look like: +
+`Token ` +
The `Authorization` value itself contains the Word “Token”. + +##Requests +This is the base URL. Please refer to endpoints to make the actual request. The base URL for all requests to the Production Shadowfax Server is given below:
+||| +|------------|-----------| +|Staging|`https://dale.staging.shadowfax.in/api`| +|Production|`https://dale.shadowfax.in/api`| + +All our API is REST-based. This means: +* It makes use of standard HTTP verbs like GET, POST, DELETE. +* The API uses standard HTTP error responses to describe errors. +* Authentication is specified with HTTP Token Authentication. + +POST data should be encoded as standard `application/json` +|Accepted Format|| +|--------------------|--------------------| +|Date|(YYYY:MM:DD)| +|DateTime |(YYYY:MM:DD HH:MM:SS)| + +##Responses +Shadowfax API uses HTTP status codes to indicate the status of your requests. This includes successful and unsuccessful responses. + +* All responses are in [JSON]('http://www.json.org') by default. +* All response Timestamps follow [ISO-8061]('https://www.w3.org/TR/NOTE-datetime') format. + > Times are expressed in UTC (Coordinated Universal Time), with a special UTC designator ("Z"). + +#### Status Codes + +* 200 - OK: Everything went as planned. +* 400 - Bad Request: You did something wrong. Often a missing argument or parameter. +* 401 - Unauthorized: Authentication was incorrect. +* 404 - Not Found +* 500 - Internal Server Error: We had a problem processing the request. +* 503 - Service Unavailable: Try again later. + +##Order States +A given order can be in one of the following states. + This state will be shared in the `status_id` field in the Order Tracking APIs. + +####Marketplace order states : + +|status_id |status |Remark |Description | +|-------------------------------|-------------------------------|--------------------------------------------------------|-------------------------------------------------------------------------------| +| `new` | New |Item New at `` | When the delivery request is new at shadowfax facility | +| `picked` | Picked |Picked |When the order is picked successfully | +| `assigned_for_seller_pickup` | Assigned For Pickup |Item Assigned For Customer Pickup at `` | When the order is Assigned to Shadowfax Rider For Seller Pickup. | +| `ofp` | Out For Pickup |Item Out For Pickup at `` | When the order is out for Seller Pickup. | +| `recd_at_rev_hub` | Received At Reverse Hub |Received at hub Successfully | When the order is Picked and Received At pickup hub | +| `item_manifested` | Item added to Bag |Item added to bag at `` | When the order is added to the bag | +| `recd_at_fwd_hub` | Received At Forward Hub |Item Received at hub at `` | when order is received at destination hub | +| `recd_at_fwd_dc` | Received At DC |Item Received at dc at `` | when the order received at destination city DC. | +| `cancelled_by_seller` | Cancelled By Seller | Item cancelled by seller at `` |Seller has requested to cancel the pickup| +| `assigned_for_delivery` | Assigned for Customer Delivery|Item Assigned at `` | Order is assigned to Shadowfax Rider for Customer Delivery. | +| `ofd` | Out For Delivery |Item OFD at `` | Order is out for Customer Delivery. | +| `delivered` | Delivered |Item Delivered at ` ` | Order has been delivered to the customer. | +| `cid` | Cid | Customer wants delivery on another date | Customer Initiated Delay(cid). Customer has requested for delivery on another day. | +| `seller_initiated_delay` | Cid |Seller Initiated Delay | Seller has requested to pickup the order on another day | +| `seller_not_contactable` | Not Contactable |Seller not reachable | Seller is not contactable for pickup | +| `nc` | Not Contactable |Refer [Appendix](#Appendix_nc) for detailed Reasons of Not Contactable | Customer is not contactable for delivery | +| `pickup_not_attempted` | Not Attempted |Not able to attempt | Not able to attempt the pickup for the order | +| `na` | Not attempted |Refer [Appendix](#Appendix_not_attempted) for detailed Reasons of Not Attempted | Customer Delivery was not attempted by Shadowfax Rider | +| `cancelled_by_customer` | Cancelled |Refer [Appendix](#Appendix_cancelled) for detailed reasons of order Cancelled. | Delivery Request has been Cancelled By customer (can be due to various reasons) | +| `rts` | Return to Seller initiated |Item Return To Seller at `` | Order is initated for Return To Seller | +| `rts_d` | Returned To Client |Item Return To Seller Delivered at `` | Order has been successfully returned to Seller. | +| `rts_in_process` | RTS In Progress |Item RTS In Progress at `` | Order Return to Seller in progress | +| `rts_ofd` | Out for Delivery for RTS |Item out for delivery to seller at `` | Order out for delivery to seller. | +| `rts_nd` | Undelivered | Item Return to Seller Not Delivered at `` | Order has been not returned to Seller. | +| `lost` | Lost |Item Lost at `` | Order lost in transit | +| `on_hold` | On Hold | Refer [Appendix](#Appendix_On_Hold) for detailed Reasons of order On Hold | When the item is put On Hold due to client orders/Operational Concerns. | +| `pickup_on_hold` | Pick up On Hold |Refer [Appendix](#Appendix_On_pickupHold) for detailed Reasons of order On Hold | Due to the specified reason the pickup was put on hold. This status is only for marketplace order type | +| `reopen_ndr` | Require Delivery - NDR |Require Delivery As Per NDR Instructions |Customer Delivery will be reattempted as per customer request | +| `in_transit_return` | In Transit for Return |Bag in transit return from ` ` |This status signifies that the bag (Master Manifest) is in transit during rts journey. | +| `bag_received` |Bag Received |Bag received at `` |This status signifies that the bag (Master Manifest) has been received at the destination facility | +| `bag_received_at_via` | Bag Received at Via |Bag received at `` |This status signifies that the bag (Master Manifest) has been received at the intermediate facility. | +| `bag_in_transit` | Bag In Transit |Bag in transit from ` ` |This status signifies that the bag (Master Manifest) is in forward journey. | +| `pincode_updated` | Pincode Updated |Item destination pincode updated at ` |This status signifies that the destination pincode was updated.| +| `item_misrouted` | Item Misrouted |Item misrouted at `` |The shipment reached to a wrong sfx facility | + +####warehouse order states : + +|status_id |status |Remark |Description | +|-------------------------------|-------------------------------|--------------------------------------------------------|-------------------------------------------------------------------------------| +| `new` | New |Item New at `` | When the delivery request is new at shadowfax facility | +| `received_from_client_warehouse` |Received from Client Warehouse |Item Received from Client Warehouse at `` | This status signifies that the item has been picked from the client's warehouse. | +| `item_manifested` | Item added to Bag |Item added to bag at `` | When the warehouse order added to the bag | +| `bag_in_transit` | Bag In Transit |Bag in transit from ` ` |This status signifies that the bag (Master Manifest) is in transit. | +| `bag_received_at_via` | Bag Received at Via |Bag received at `` |This status signifies that the bag (Master Manifest) has been received at the intermediate facility. | +| `bag_received` |Bag Received |Bag received at `` |This status signifies that the bag (Master Manifest) has been received at the destination facility | +| `ofd` | Out For Delivery |Item OFD at `` | Order is out for Customer Delivery. | +| `cid` | Cid | Customer wants delivery on another date | Customer Initiated Delay(cid). Customer has requested for delivery on another day. | +| `on_hold` | On Hold | Refer [Appendix](#Appendix_On_Holdwarehouse) for detailed Reasons of order On Hold | When the item is put On Hold due to client orders/Operational Concerns. | +| `reopen_ndr` | Require Delivery - NDR |Require Delivery As Per NDR Instructions |Customer Delivery will be reattempted as per customer request | +| `delivered` | Delivered |Item Delivered at ` ` | Order has been delivered to the customer. | +| `rto` |Return to origin initiated |Item Return To Origin at `` | Order is initated for Return To Client/ warehouse| +| `rto_d` | Returned to Client |Item Return To Origin Delivered at `` | Order has been successfully returned to client | +| `lost` | Lost |Item Lost at `` | Order lost in transit | +| `item_misrouted` | Item Misrouted |Item misrouted at `` |The shipment reached to a wrong sfx facility | +| `pincode_updated` | Pincode Updated |Item destination pincode updated at ` |This status signifies that the destination pincode was updated.| +| `in_transit_return` | In Transit for Return |Bag in transit return from ` ` |This status signifies that the bag (Master Manifest) is in transit during rts journey. | +| `recd_at_fwd_dc` | Received At DC |Item Received at dc at `` | Order is received to Forward DC. | +| `recd_at_fwd_hub` | Received At Forward Hub |Item Received at hub at `` | Order is received at Forward Hub from Reverse Hub. | +| `assigned_for_delivery` | Assigned for Customer Delivery|Item Assigned at `` | Order is assigned to Shadowfax Rider for Customer Delivery. | +| `nc` | Not Contactable |Refer [Appendix](Appendix_ncwarehouse) for detailed Reasons of Not Contactable | Customer is not contactable for delivery | +| `na` | Not attempted |Refer [Appendix](#Appendix_not_attempted) for detailed Reasons of Not Attempted | Customer Delivery was not attempted by Shadowfax Rider | +| `cancelled_by_customer` | Cancelled |Refer [Appendix](#Appendix_cancelled) for detailed reasons of order Cancelled. | Delivery Request has been Cancelled By customer (can be due to various reasons) | + + +##Push Callback API +We have built a generic PUSH API callback for both Marketplace(seller pickup) and warehouse models. The client can enable it to get real-time notification of each push notification status change. + +### Callback Header details + +The Standard callback header has the following keys: `Authentication Key(Non mandatory).` + + ++ Headers + + Authorization: Token c6b780ae9250f3 + Content-Type: application/json + +Here we have the capability to add/modify the header keys as shown in the example below: + ++ Headers + + Content-Type: application/json + Authorization: d5eecf30ae6eb8d158dc54c77178ab9 + account_name: SF_ACC_STAGING + + +### Request Body details +The callback contains the below parameters: + +* `awb_number` - Generated by Shadowfax while placing an order. +* `order_id` - client_order_id shared while placing an order. +* `event_timestamp` - event_timestamp of the current status. +* `current_location` - The current location of the order during the event. +* `comments` - Contains the remarks associated with the status. +* `status` - Current status of the shipment +* `event` - Current status id of the shipment +* `otp_verifed` - The value indicates whether the status was marked after OTP verification. The possible values are `"Y"/"N"/"NA"`. By Default `"NA"`. +* `rider_name` - The name of the rider delivering the order.The rider details are shared when the current status is Out For Delivery (ofd). +* `rider_contact` - The contact details of the rider delivering the order.The rider details are shared when the current status is Out For Delivery (ofd). +* `type` - The order type is shared over this field. eg: FWD/REV +* `recepient_info` - This field contains proof of delivery, and it will be sent only when the current status has been delivered or RTS_D has been confirmed. + + +This is the structure of the payload data(JSON) that will be pushed to your end. + ++ Response 200 (application/json) + + { + "awb_number": "SF610198449AAA", + "order_id": "FL20230517311447364", + "event_timestamp": "2023-05-18 16:22:13", + "current_location": "CHN_Ambattur_EXP", + "comments": "Item Delivered at CHN_Ambattur_EXP", + "event": "delivered", + "status": "Delivered", + "otp_verified": "Y", + "rider_name": null, + "rider_contact": null, + "client_id": 2245, + "recipient_info": { + "recipient": "Customer", + "recipient_name": "Chittaranjan", + "recipient_contact": "20369854715", + "recipient_signature": [] + }, + "type": "FWD" + } +When we want to send a push message to your server, we need to make an API call to a push service. API call would require the following callback details, which can be configured via the Client Portal on the Webhook tab. +#### Note : Customized Request Body for Push Callback + +We also have the capability to send the below optional fields on clients request as an addition to the above request body : + +* `updated_date` - Here the value would be the event updated date and the format would be `YYYY-MM-DD` eg: `2023-05-18` +* `updated_time` - Here the value would be the event updated time. The format would be `HHMM`. eg: `1622` +* `created_date` - Here the value would be the order created date. The format would be `YYYY-MM-DD` eg: `2023-05-18`. +* `created_time` - Here the value would be the order created time. The format would be `HHMM`. eg: `1622` +* `total_amount` - This field would be the total amount (inclusive of GST) passed during order placement. +* `destinationPincode` - This field would have the delivery pincode. +* `destinationCity` - This field would have the drop city name. default: `null` +* `originCity` - This field would have the drop city name. +* `shipmenttype` - This field indicated the order leg, that is for reverse leg the value would be `R` and for forward leg the value would be `F`. This field is not the same as the `type` field. +* `payMode` - Here the value indicates the payment mode. The value would be `C` for cod mode and `P` for prepaid mode. +* `codAmount` - This field would have the cod amount. +* `estimatedDeliveryDate` - This field would have the promised date. The format would be `YYYY-MM-DD HH:MM:SS`. +* `attempt_number` - This field would have the attempt count. default: 0 + +`The appropriate URL implemented at your end to trigger a push message(Mandatory).` + + + +####Marketplace order states for PUSH Api : + +|status_id |status |Remark |Description | +|-------------------------------|-------------------------------|--------------------------------------------------------|-------------------------------------------------------------------------------| +| `assigned_for_pickup` | Assigned For Pickup |Item Assigned For Seller Pickup at `` | When the Item Assigned For Seller Pickup | +| `assigned_for_seller_pickup` | Assigned For Pickup |Item Assigned For Customer Pickup at `` | When the order is Assigned to Shadowfax Rider For Seller Pickup. | +| `ofp` | Out For Pickup |Item Out For Pickup at `` | When the order is out for Seller Pickup. | +| `picked` | Picked |Picked |When the order is picked successfully | +| `recd_at_rev_hub` | Received At Reverse Hub |Received at hub Successfully | When the order is Picked and Received At pickup hub | +| `recd_at_fwd_hub` | Received At Forward Hub |Item Received at hub at `` | when order is received at destination hub | +| `recd_at_fwd_dc` | Received At DC |Item Received at dc at `` | when the order received at destination city DC. | +| `ofd` | Out For Delivery |Item OFD at `` | Order is out for Customer Delivery. | +| `assigned_for_delivery` | Assigned for Customer Delivery|Item Assigned at `` | Order is assigned to Shadowfax Rider for Customer Delivery. | +| `delivered` | Delivered |Item Delivered at ` ` | Order has been delivered to the customer. | +| `cid` | Cid | Customer wants delivery on another date | Customer Initiated Delay(cid). Customer has requested for delivery on another day. | +| `nc` | Not Contactable |Refer [Appendix](#Appendix_pushnc) for detailed Reasons of Not Contactable | Customer is not contactable for delivery | +| `na` | Not attempted |Refer [Appendix](#Appendix_pushnot_attempted) for detailed Reasons of Not Attempted | Customer Delivery was not attempted by Shadowfax Rider | +| `pickup_not_attempted` | Not Attempted |Not able to attempt | Not able to attempt the pickup for the marketplace order | +| `pickup_on_hold` | Pick up On Hold |Refer [Appendix](#Appendix_On_pushpickupHold) for detailed Reasons of order On Hold | Due to the specified reason the pickup was put on hold| +| `reopen_ndr` | Require Delivery - NDR |Require Delivery As Per NDR Instructions |Customer Delivery will be reattempted as per customer request | +| `cancelled_by_customer` | Cancelled |Refer [Appendix](#Appendix_pushmarketcancelled) for detailed reasons of order Cancelled. | Delivery Request has been Cancelled By customer (can be due to various reasons) | +| `rts` | Return to Seller initiated |Item Return To Seller at `` | Order is initated for Return To Seller | +| `rts_in_process` | RTS In Progress |Item RTS In Progress at `` | Order Return to Seller in progress | +| `rts_d` | Returned To Client |Item Return To Seller Delivered at `` | Order has been successfully returned to Seller. | +| `rts_nd` | Undelivered | Item Return to Seller Not Delivered at `` | Order has been not returned to Seller. | +| `lost` | Lost |Item Lost at `` | Order lost in transit | +| `on_hold` | On Hold | Refer [Appendix](#Appendix_pushOn_Holdmarketplace) for detailed Reasons of order On Hold | When the item is put On Hold due to client orders/Operational Concerns. | +| `seller_initiated_delay` | Cid |Seller Initiated Delay | Seller has requested to pickup the order on another day | +| `cancelled_by_seller` | Cancelled by Seller |Item cancelled by seller at ``| Seller has requested to cancel the pickup| + + +####Warehouse order states for PUSH Api : + +|status_id |status |Remark |Description | +|-------------------------------|-------------------------------|--------------------------------------------------------|-------------------------------------------------------------------------------| +| `received_from_client_warehouse` |Received from Client Warehouse |Item Received from Client Warehouse at `` | This status signifies that the item has been picked from the client's warehouse. | +| `recd_at_fwd_dc` | Received At DC |Item Received at dc at `` | Order is received to Forward DC. | +| `recd_at_fwd_hub` | Received At Forward Hub |Item Received at hub at `` | Order is received at Forward Hub from Reverse Hub. | +| `assigned_for_delivery` | Assigned for Customer Delivery|Item Assigned at `` | Order is assigned to Shadowfax Rider for Customer Delivery. | +| `ofd` | Out For Delivery |Item OFD at `` | Order is out for Customer Delivery. | +| `delivered` | Delivered |Item Delivered at ` ` | Order has been delivered to the customer. | +| `cancelled_by_customer` | Cancelled |Cancelled | Delivery Request has been Cancelled By customer (can be due to various reasons) | +| `cid` | Cid | Customer wants delivery on another date | Customer Initiated Delay(cid). Customer has requested for delivery on another day. | +| `nc` | Not Contactable |Customer not contactable | Customer is not contactable for delivery | +| `na` | Not attempted | Not Attempted | Customer Delivery was not attempted by Shadowfax Rider | +| `lost` | Lost |Item Lost at `` | Order lost in transit | +| `reopen_ndr` | Require Delivery - NDR |Require Delivery As Per NDR Instructions |Customer Delivery will be reattempted as per customer request | +| `rto` |Return to origin initiated |Refer [Appendix](#Appendix_pushrtowarehouse) for detailed Reasons of Return to origion | Order is initated for Return To Client/ warehouse| +| `rto_in_process` |In RTO/RTS Process |Item RTO In Progress at `` | Order is initated for Return To Client/ warehouse is in process| +| `rto_d` | Returned to Client |Item Return To Origin Delivered at `` | Order has been successfully returned to client | +| `rto_nd` | Undelivered |Item Return To Origin Not Delivered at `` | Order has been returned to client | +| `on_hold` | On Hold | Refer [Appendix](#Appendix_pushOn_Holdwarehouse) for detailed Reasons of order On Hold | When the item is put On Hold due to client orders/Operational Concerns. | + +# Group Serviceability + + +## Unified Pincode Serviceability [/v1/clients/serviceability/?service={service}&page={page}&count={count}&pincodes={pincodes}] +### List All Serviceable Pincodes [GET] + +By using this endpoint, you can get a list of all Shadowfax serviceable pincodes, as well as the services provided by each of them. + +If pagination is not sent as a parameter then by default 25 pincodes will be retrieved. + +While triggering the API enter the service for which the list of pincode needs to be fetched. +* The service parameter can be: + * `seller_pickup` - List all the pincodes where seller pickup is available. + * `customer_delivery` - List all the pincodes where customer delivery is available. + * `customer_pickup` - List all the pincodes where customer pickup is available. + * `seller_delivery` - List all the pincodes where seller delivery is available. + * `warehouse_pickup` - List all the pincodes where warehouse pickup is available. + * `warehouse_return` - List all the pincodes where warehouse return or rto is available. + + +##Requests +This is the URL to make an actual request:
+||| +|------------|-----------| +|Staging|`https://dale.staging.shadowfax.in/api/v1/clients/serviceability/?service=customer_delivery&page=1&count=10&pincodes=560016,560017`| +|Production|`https://dale.shadowfax.in/api/v1/clients/serviceability/?service=customer_delivery&page=1&count=10&pincodes=560016,560017`| + + ++ Parameters + + + service:customer_delivery (required, string) - Enter the service for which the list of pincodes needs to be fetched. + + page:1 (number)- Enter the page number. + + count:10 (number) - Enter the per page count. + + pincodes:560017 (number) - Enter the pincodes. + + ++ Request (application/json) + + + Headers + + Authorization: Token + ++ Response 200 (application/json) + + [ + { + "code": 560017, + "services": [ + "Regular", + "Surface" + ] + } + { + "code": 560016, + "services": [ + "Regular" + ] + } + ] + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 400 (application/json) + + { + "message": "Invalid Request" + } + + + +# group AWB Generation +## AWB Generation [/v3/clients/generate_marketplace_awb/] + +### Generate AWB [POST] +We have an automated process of sharing new and unused AWBs once the existing ones are about to exhaust. +It takes the count of AWB Number as the parameter. The endpoint automatically generates new AWBs and returns them if the unused AWBs are less than the required count. + The max limit for the number of AWBs that can be requested in one go is 100,000. + +For any changes, please reach out to Shadowfax's Account Manager. +||| +|------------|-----------| +|Staging|`https://dale.staging.shadowfax.in/api/`| + +|Production|`https://dale.shadowfax.in/api/`| + + ++ Request + + + Body + + { + "count" : 10 + } + + + Headers + + Authorization: Token Authorization + ++ Response 200 (application/json) + + { + "message": "Success", + "awb_numbers": [ + "SF10000001san", + "SF10000002san", + "SF10000003san", + "SF10000004san", + "SF10000005san", + "SF10000006san", + "SF10000007san", + "SF10000008san", + "SF10000009san", + "SF10000010san" + ] + } + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 400 (application/json) + + { + "message": "Max limit on count is 100000. Please input a lower integer" + } + ++ Response 400 (application/json) + + { + "message": "Invalid client type." + } + ++ Response 400 (application/json) + + { + "message": "Client does not exist in forward. " + } + + + +#group Marketplace- Order Creation +## Order Creation for marketplace seller model [/v3/clients/orders/] +### Create a Seller Pickup Request[POST] +This endpoint is used to place a new seller pickup and delivery order in our system. +It takes order_details, customer_details, pickup_details, rts_details and product_details as parameters. + +Below given are sample pincodes for testing in Staging Environment for Pickup / delivered / return +* 110009 +* 560077 +* 560007 + ++ Parameters + + format (optional, string) + Type of response (json or xml) required + + Default: json + ++ Request (application/json) + + + Body + + { + "order_type": "marketplace", + "order_details": { + "client_order_id": "0123", + "actual_weight": 100, + "volumetric_weight": 100, + "product_value": 515, + "payment_mode": "Prepaid", + "cod_amount": "0", + "promised_delivery_date": "2018-01-09T00:00:00.000Z", + "total_amount": 5555, + "eway_bill":"7452128", + "gstin_number" : "74185263", + "order_service":"regular" + }, + "customer_details": { + "name": "Deepak", + "contact": "9999999999", + "address_line_1": "Flat 38 Loknayak Aptt Sector 9 Rohini", + "address_line_2": "Sector 7, dividing road", + "city": "New Delhi", + "state": "Delhi", + "pincode": 110001, + "alternate_contact": "9999999999", + "latitude": "11.0000", + "longitude": "77.0000" + }, + "pickup_details": { + "name": "HSR Telecom", + "contact": "9999999999", + "address_line_1": "8th main 5th cross", + "address_line_2": "Teachers colony", + "city": "Bengaluru", + "state": "Karnataka", + "pincode": 560007, + "latitude": "12.0000", + "longitude": "78.0000", + "unique_code" : "warehousecode01" + }, + "rts_details": { + "name": "Deepak", + "contact": "9999999999", + "address_line_1": "sec-24 poc-25 Hno-76", + "address_line_2": "Shiv mandir", + "city": "Ludhiana", + "state": "Punjab", + "pincode": 110009, + "email": "abc@gmail.com", + "latitude": "12.0000", + "longitude": "78.0000", + "unique_code" : "warehousecode01" + }, + "product_details": [ + { + "hsn_code": "HJUY890", + "invoice_no": "SNP678", + "sku_name": "macBook Air", + "sku_id": "MAC789", + "category": "cosmic", + "price": 4500, + "seller_details": { + "seller_name": "NewSeller", + "seller_address": "Arangath House, Manalithra, Thrissur Dist", + "seller_state": "Kerala", + "gstin_number": "HDS798HG" + }, + "taxes": { + "cgst": 3, + "sgst": 4, + "igst": 5, + "total_tax": 5.6 + }, + "additional_details": { + "requires_extra_care": "False", + "type_extra_care": "Dangerous Goods", + "quantity" : 2 + } + } + ] + } + + + + Headers + + Authorization: Token + + + Attributes + + + order_type (required, string)-For Marketplace model integration, value will be "marketplace" + + order_details (required, object) + + client_order_id (required, string) - Order ID in the client system. Character limit : 100 + + awb_number(optional, string) - Awb Number provided by you (Optional, If not provided, an awb number will be assigned automatically ) Character limit : 100 + + actual_weight(optional, number) - Weight of the Product (in gms) default :0.0 + + volumetric_weight(optional, number) - Volumetric Weight of the Product (in gms). default : 0.0 (volumetric weight=(l*b*h)/5000) + + product_value (required, number) - Total SKU's product value exc. tax + + cod_amount (required, number) - Amount to be collected from the customer. + + payment_mode (required, string) - either COD or Prepaid (Mandatory) + + promised_delivery_date (optional, string) - scheduled delivery date of the product (Format – yyyy-mm- dd, e.g. 2016-11- 09) + + total_amount (optional, number) - Total SKU's value inclusive of tax. + + eway_bill (optional , string ) - Character limit : 12 + + gstin_number (optional, string) - Character limit : 50 + + order_service (optional, string)- The order service will be regular + + customer_details (required, object) + + name (required, string) - Character limit : 100 + + contact (required, string) - 10(minimum) digit phone number. Character limit : 13 + + alternate_contact (optional, string) - Pickup Customer's Alternate Contact Number. If provided, it should be a minimum of 10 digits otherwise validation error will be thrown.Character limit :13 + + address_line_1 (required, string) + + address_line_2 (optional ,string) + + city (required, string) - customer city.Character limit :50 + + state (required, string) - customer state. Character limit :50 + + pincode (required, number) - 6 digit valid pincode Character limit :6 + + latitude (optional, string) + + longitude (optional, string) + + pickup_details (required, object ) + + name (optional, string)-Character limit : 100 + + contact (required, string) : 10(minimum) digit phone number.Character limit : 13 + + address_line_1 (required, string) + + address_line_2 (optional, string) + + city (required, string) - customer city .Character limit : 50 + + state (required, string) - customer state. Character limit : 50 + + pincode (required, number) - 6 digit valid pincode + + latitude (optional, string) + + longitude (optional, string) + + unique_code (optional, string) - 255 (max length) a unique warehouse code + + rts_details (required , object) + + name (required, string)- Character limit : 100 + + contact (required, string) - 10(minimum) digit phone number.Character limit : 13 + + address_line_1 (required, string) + + address_line_2 (optional, string) + + city (required, string) - customer city. Character limit : 50 + + state (required, string) - customer state. Character limit : 50 + + pincode (required, number) - 6 digit valid pincode + + email(optional , string) - 100 (max length) valid email address + + latitude (optional, string) + + longitude (optional, string) + + unique_code (optional, string) - 255 (max length) a unique warehouse code. + + product_details (required , array) - Array of json objects. This can have multiple skus but the array should contain at least one valid sku object. + + (object) + + sku_id (optional, string).Character limit : 100 + + sku_name (required, string) - Name of the product + + hsn_code (optional, string) - Character limit :50 + + invoice_no (optional, string)- Character limit : 50 + + category (optional, string)-Character limit : 200 + + price (required, number) + + seller_details (optional, object) + + seller_name (optional, string) -Character limit : 100 + + seller_address (optional, string) + + seller_state (optional, string) - Character limit : 50 + + gstin_number (optional, string) -Character limit : 50 + + taxes (optional, object) + + cgst (optional , number) + + sgst (optional , number) + + igst (optional, number) + + total_tax (optional , number ) + + additional_details (optional, object) + + requires_extra_care (optional, string) + + type_extra_care (optional, string) + + quantity (optional, number) + ++ Response 200 (application/json) + + { + "message": "Success", + "errors": null, + "data": { + "id": 2199782, + "client_name": Test Client Grocery", + "client_order_id": "3399845736", + "awb_number": "SF401022456TST", + "product_value": 900.0, + "cod_amount": 0, + "payment_mode": "prepaid", + "order_date": "2023-08-02", + "promised_delivery_date": null, + "status_display": "New", + "status": "new", + "pickup_details": { + "name": "Titanic world", + "contact": "8319733588", + "address_line_1": "World wide center", + "address_line_2": "USA", + "city": "Bengaluru", + "state": "Karnataka", + "pincode": 560007, + "alternate_contact": null, + "sms_contact": null, + "latitude": "", + "longitude": "" + }, + "delivery_details": { + "name": "Vikas", + "contact": "8319733588", + "address_line_1": "D-8,sector-,Delhi", + "address_line_2": "", + "city": "Delhi", + "state": "Delhi", + "pincode": 560007, + "alternate_contact": "9090909090", + "sms_contact": null, + "latitude": "", + "longitude": "" + }, + "product_details": [ + { + "sku_id": "MAC789", + "sku_name": "macBook Air", + "price": 9000.0, + "return_reason": null, + "category": "Electronic", + "brand": null, + "additional_details": "{\"requires_extra_care\": true, \"type_extra_care\": null, \"quantity\": 2}", + "seller_name": "NewSeller", + "seller_address": "Arangath House, Manalithra, Thrissur Dist", + "seller_state": "Kerala", + "hsn_code": "HJUY890", + "invoice_no": "SNP678", + "sgst_amount": 68.0, + "cgst_amount": 68.0, + "igst_amount": 0.0, + "gstin_number": "", + "total_tax_value": 136.0, + "qc_required": true, + "qc_rules": "" + }, + { + "sku_id": "MAC790", + "sku_name": "macBook Air", + "price": 9000.0, + "return_reason": null, + "category": "Electronic", + "brand": null, + "additional_details": "{\"requires_extra_care\": true, \"type_extra_care\": null, \"quantity\": 2}", + "seller_name": "NewSeller", + "seller_address": "Arangath House, Manalithra, Thrissur Dist", + "seller_state": "Kerala", + "hsn_code": "HJUY890", + "invoice_no": "SNP678", + "sgst_amount": 68.0, + "cgst_amount": 68.0, + "igst_amount": 0.0, + "gstin_number": "", + "total_tax_value": 136.0, + "qc_required": true, + "qc_rules": "" + }, + { + "sku_id": "MAC791", + "sku_name": "macBook Air", + "price": 9000.0, + "return_reason": null, + "category": "Electronic", + "brand": null, + "additional_details": "{\"requires_extra_care\": true, \"type_extra_care\": null, \"quantity\": 5}", + "seller_name": "NewSeller", + "seller_address": "Arangath House, Manalithra, Thrissur Dist", + "seller_state": "Kerala", + "hsn_code": "HJUY890", + "invoice_no": "SNP678", + "sgst_amount": 68.0, + "cgst_amount": 68.0, + "igst_amount": 0.0, + "gstin_number": "", + "total_tax_value": 136.0, + "qc_required": true, + "qc_rules": "" + } + ], + "eway_bill_number": null, + "invoice_date": null, + "sort_code": null + } + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Order for COID : Test warhosue32x0232 is already created with AWB : SF325110257MY", + "COID": "Test warhosue32x0232", + "AWB": "SF325110257MY" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer Contact number is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "RTO Contact Number is Invalid" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Please Enter Valid 12-digit E-Way Bill Number" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Invalid order service" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "GSTIN Number is mandatory for product value more than 50000" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Invalid Pickup Pincode. Pickup pincode 128888 is not serviceable" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Invalid Return Pincode. Pickup pincode 128888 is not serviceable" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Invalid Delivery Pincode. Pincode 128888 is not serviceble" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Invalid email format for seller email" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": { + "order_details": { + "client_order_id": [ + "This field is required." + ] + } + }, + "data": {} + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "COD amount is greater than 50000 and Unable to create E-way Bill" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Payment Mode is Required" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Product value is required" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Return details are Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Pickup details are Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer Pincode is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer details are Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Pickup Address is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Pickup Contact number is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Pickup unique code is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Return Pincode is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Return Name is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Return address is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer details are Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer Pincode is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer Contact number is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer Contact number is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer Delivery Address is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer Delivery Address is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Invalid Customer contact number" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Customer contact is not valid" + ] + } + ++ Response 200 (application/json) + { + "message": "Failure", + "errors": { + "order_details": { + "awb_number": [ + "order with this awb number already exists." + ] + } + }, + "data": {} + } + ++ Response 400 (application/json) + + {"message":"Cannot place an Order: Please clear your pending invoices to start placing order again"} + + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 500 (application/json) + + { + "message": "Internal Server Error" + } + + +#group Warehouse - Order Creation +## Order Creation for warehouse model [/v3/clients/orders/] +### Create a Delivery Request[POST] +This endpoint is used to create a new delivery request in our E-Commerce Forward System. It takes customer details, product details, and RTO(Return To Origin) details as parameters. +Make sure to only add serviceable pincodes. + +Below given are sample pincodes for testing in Staging Environment for Pickup / delivered / return +* 110009 +* 560077 +* 560007 + ++ Parameters + + format (optional, string) + Type of response (json or xml) required + + Default: json + ++ Request (application/json) + + + Body + + { + "order_type": "warehouse", + "order_details": { + "client_order_id": "0123", + "actual_weight": 100, + "volumetric_weight": 100, + "product_value": 515, + "payment_mode": "Prepaid", + "cod_amount": "0", + "promised_delivery_date": "2018-01-09T00:00:00.000Z", + "total_amount": 5555, + "eway_bill": "7452128", + "gstin_number": "HDS798HG", + "order_service":"regular" + }, + "customer_details": { + "name": "Deepak", + "contact": "9999999999", + "address_line_1": "Flat 38 Loknayak Aptt Sector 9 Rohini", + "address_line_2": "Sector 7, dividing road", + "city": "New Delhi", + "state": "Delhi", + "pincode": 110001, + "alternate_contact": "9999999999", + "latitude": "11.0000", + "longitude": "77.0000", + "location_type" : "residential" + }, + "pickup_details": { + "name": "HSR Telecom", + "contact": "9999999999", + "address_line_1": "8th main 5th cross", + "address_line_2": "Teachers colony", + "city": "Bengaluru", + "state": "Karnataka", + "pincode": 560007, + "latitude": "12.0000", + "longitude": "78.0000", + "unique_code" : "warehousecode01" + }, + "rto_details": { + "name": "Deepak", + "contact": "9999999999", + "address_line_1": "sec-24 poc-25 Hno-76", + "address_line_2": "Shiv mandir", + "city": "Ludhiana", + "state": "Punjab", + "pincode": 110009, + "unique_code" : "testwarehouse01" + }, + "product_details": [ + { + "hsn_code": "HJUY890", + "invoice_no": "SNP678", + "sku_name": "macBook Air", + "sku_id": "MAC789", + "category": "cosmic", + "price": 4500, + "seller_details": { + "seller_name": "NewSeller", + "seller_address": "Arangath House, Manalithra, Thrissur Dist", + "seller_state": "Kerala", + "gstin_number": "HDS798HG" + }, + "taxes": { + "cgst": 3, + "sgst": 4, + "igst": 5, + "total_tax": 5.6 + }, + "additional_details": { + "requires_extra_care": "False", + "type_extra_care": "Dangerous Goods", + "quantity" : 2 + } + } + ] + } + + + Headers + + Authorization: Token + + + Attributes + + + order_type (required, string)-For Warehouse model integration, value will be "warehouse" + + order_details (required, object) + + client_order_id (required, string) - Order ID in the client system. Character limit : 100 + + awb_number(optional, string) - Awb Number provided by you (Optional, If not provided, an awb number will be assigned automatically ). Character limit : 100 + + actual_weight(optional, number) - Weight of the Product (in gms). default : 0.0 + + volumetric_weight(optional, number) - Volumetric Weight of the Product. default : 0.0 (volumetric weight=(l*b*h)/5000) + + product_value (required, number) - Total SKU's product value exc. tax + + cod_amount (required, number) - Amount to be collected from the customer. + + payment_mode (required, string) - either COD or Prepaid (Mandatory) + + promised_delivery_date (optional, string) - scheduled delivery date of the product (Format – yyyy-mm- dd, e.g. 2016-11- 09) + + total_amount (optional, number) - Total SKU's value inclusive of tax. + + gstin_number (optional, string) - Character limit : 50 + + eway_bill (optional, number) - Character limit : 12 + + order_service (optional, string)- The order service will be regular + + customer_details (required, object) + + name (required, string)- Character limit :100 + + contact (required, string) - 10(minimum) digit phone number. Character limit : 13 + + alternate_contact (optional, string) - Pickup Customer's Alternate Contact Number. If provided, it should be a minimum of 10 digits otherwise validation error will be thrown.Character limit :13 + + address_line_1 (required, string) + + address_line_2 (optional ,string) + + city (required, string) - customer city. Character limit : 50 + + state (required, string) - customer state. Character limit : 50 + + pincode (required, number) - 6 digit valid pincode. Character limit : 6 + + latitude (optional, string) + + longitude (optional, string) + + location_type (optional, string) - "residential"/"Commercial" + + pickup_details (required, object ) + + name (optional, string)- Character limit : 100 + + contact (required, string) : 10(minimum) digit phone number. Character limit : 13 + + address_line_1 (required, string) + + address_line_2 (optional, string) + + city (required, string) - customer city. Character limit : 50 + + state (required, string) - customer state. Character limit : 50 + + pincode (required, number) - 6 digit valid pincode. Character limit : 6 + + latitude (optional, string) + + longitude (optional, string) + + unique_code (optional, string) - 255 (max length) a unique warehouse code + + rto_details (required, object) - Address and Contact Details of the location where RTO needs to be done + + name (required, string) - Character limit : 100 + + contact (required, string) - 10(minimum) digit phone number. Character limit : 13 + + address_line_1 (required, string) + + address_line_2 (optional ,string) + + city (required, string) - return warehouse city. Character limit : 50 + + state (required, string) - return warehouse state. Character limit : 50 + + pincode (required, number) - 6 digit valid pincode. Character limit : 6 + + unique_code (optional, string) - 255 (max length) a unique warehouse code + + product_details (required , array) - Array of json objects. This can have multiple skus but the array should contain at least one valid sku object. + + (object) + + client_sku_id (optional, string) - Character limit : 100 + + sku_name (required, string) - Name of the product + + hsn_code (optional, string) - Character limit : 50 + + invoice_no (optional, string) - Character limit : 50 + + category (optional, string) - Character limit : 200 + + price (required, number) + + seller_details (optional, object) + + seller_name (optional, string) - Character limit : 100 + + seller_address (optional, string) + + seller_state (optional, string). Character limit : 50 + + gstin_number (optional, string). Character limit : 50 + + taxes (optional, object) + + cgst (optional , number) + + sgst (optional , number) + + igst (optional, number) + + total_tax (optional , number ) + + additional_details (optional, object) + + requires_extra_care (optional, string) + + type_extra_care (optional, string) + + quantity (optional, number) + ++ Response 200 (application/json) + + { + "message": "Success", + "errors": null, + "data": { + "id": 2035397, + "client_name": "Treesa M Antony", + "client_order_id": "0123", + "awb_number": "SF36089989TMA", + "product_value": 515.0, + "cod_amount": 0, + "payment_mode": "prepaid", + "order_date": "2022-01-28", + "promised_delivery_date": "2018-01-08T18:30:00Z", + "status_display": "New", + "status": "new", + "pickup_details": { + "name": "HSR Telecom", + "contact": "9999999999", + "address_line_1": "8th main 5th cross", + "address_line_2": "Teachers colony", + "city": "Bengaluru", + "state": "Karnataka", + "pincode": 560007, + "alternate_contact": null, + "sms_contact": null, + "latitude": "12.0000", + "longitude": "78.0000" + }, + "delivery_details": { + "name": "Deepak", + "contact": "9999999999", + "address_line_1": "Flat 38 Loknayak Aptt Sector 9 Rohini", + "address_line_2": "Sector 7, dividing road", + "city": "New Delhi", + "state": "Delhi", + "pincode": 110001, + "alternate_contact": "9999999999", + "sms_contact": null, + "latitude": "11.0000", + "longitude": "77.0000" + }, + "product_details": [ + { + "sku_id": "MAC789", + "sku_name": "macBook Air", + "price": 4500.0, + "return_reason": null, + "category": "cosmic", + "brand": null, + "additional_details": "{\"type_extra_care\": \"Dangerous Goods\", \"requires_extra_care\": false}", + "seller_name": "NewSeller", + "seller_address": "Arangath House, Manalithra, Thrissur Dist", + "seller_state": "Kerala", + "hsn_code": "HJUY890", + "invoice_no": "SNP678", + "sgst_amount": 4.0, + "cgst_amount": 3.0, + "igst_amount": 5.0, + "gstin_number": "HDS798HG", + "total_tax_value": 5.6, + "qc_required": true, + "qc_rules": "" + } + ], + "eway_bill_number": "7452128", + "invoice_date": null, + "sort_code": null + } + } + + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Order for COID : Test warhosue32x0232 is already created with AWB : SF325110257MY", + "COID": "Test warhosue32x0232", + "AWB": "SF325110257MY" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "COD amount is greater than 50000 and Unable to create E-way Bill" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Payment Mode is Required" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Product value is required" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Customer Contact number is Required" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "RTO Contact Number is Invalid" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Please Enter Valid 12-digit E-Way Bill Number" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Invalid order service" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "GSTIN Number is mandatory for product value more than 50000" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Invalid Pickup Pincode. Pickup pincode 128888 is not serviceable" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Invalid Return Pincode. Pickup pincode 128888 is not serviceable" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": "Invalid Delivery Pincode. Pincode 128888 is not serviceble" + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": [ + "Invalid email format for seller email" + ] + } + ++ Response 200 (application/json) + + { + "message": "Failure", + "errors": { + "order_details": { + "client_order_id": [ + "This field is required." + ] + } + }, + "data": {} + } + ++ Response 200 (application/json) + { + "message": "Failure", + "errors": { + "order_details": { + "awb_number": [ + "order with this awb number already exists." + ] + } + }, + "data": {} + } + ++ Response 400 (application/json) + + { "message":"Cannot place an Order: Please clear your pending invoices to start placing order again"} + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + + ++ Response 500 (application/json) + + { + "message": "Internal Server Error" + } + + + +# Group Order Details + +## Single Order Details V4 version [/v4/clients/orders/{awb_number}/track/] + ++ Parameters + + awb_number (required, string) ... AWB Number of the order + +### Get Details of a Request[GET] + +This endpoint is used to get all details of an Order by AWB Number. The v4 version of tracking API provides the additional intermediate statuses like Item Manifested, Bag In Transit etc. ++ Request + + + Header + + Authorization: Token Authorization + ++ Response 200 (application/json) + + { + "message": "Success", + "order_details": { + "id": 145475249, + "client_name": "Test Client", + "client_order_id": "11002", + "awb_number": "SF222344412TST", + "product_value": 5555.0, + "cod_amount": 0, + "payment_mode": "prepaid", + "order_date": "2022-02-28", + "promised_delivery_date": null, + "status_display": "Undelivered", + "status": "rts_nd", + "pickup_details": { + "name": "HSR Telecom", + "contact": "9999999999", + "address_line_1": "8th main 5th cross", + "address_line_2": "Teachers colony", + "city": "Bengaluru", + "state": "Karnataka", + "pincode": 999999, + "alternate_contact": null, + "sms_contact": null, + "latitude": "12.0000", + "longitude": "78.0000" + }, + "delivery_details": { + "name": "Deepak", + "contact": "1234567890", + "address_line_1": "Flat 38 Loknayak Aptt Sector 9 Rohini", + "address_line_2": "Sector 7, dividing road", + "city": "New Delhi", + "state": "Delhi", + "pincode": 999998, + "alternate_contact": "1234567890", + "sms_contact": null, + "latitude": "11.0000", + "longitude": "77.0000" + }, + "product_details": [ + { + "sku_id": null, + "sku_name": "macBook Air", + "price": 4500.0, + "return_reason": null, + "category": "cosmic", + "brand": null, + "additional_details": "{\"requires_extra_care\": false, \"type_extra_care\": \"Dangerous Goods\"}", + "seller_name": "NewSeller", + "seller_address": "Arangath House, Manalithra, Thrissur Dist", + "seller_state": "Kerala", + "hsn_code": "HJUY890", + "invoice_no": "SNP678", + "sgst_amount": 4.0, + "cgst_amount": 3.0, + "igst_amount": 5.0, + "gstin_number": "HDS798HG", + "total_tax_value": 5.6, + "qc_required": true, + "qc_rules": "" + } + ], + "eway_bill_number": null, + "invoice_date": null, + "customer_track_url": "https://exp.shadowfax.in/JdDYM6" + }, + "tracking_details": [ + { + "created": "2022-02-28T08:58:57Z", + "location": "SFX TestHub", + "status_id": "new", + "status": "New", + "remarks": "Item New at SFX TestHub", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:00:03Z", + "location": "SFX TestHub", + "status_id": "assigned_for_seller_pickup", + "status": "Assigned For Seller Pickup", + "remarks": "Item assigned for seller pickup at SFX TestHub", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:00:09Z", + "location": "SFX TestHub", + "status_id": "ofp", + "status": "Out For Pickup", + "remarks": "Item Out For Pickup at SFX TestHub", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:00:15Z", + "location": "SFX TestHub", + "status_id": "recd_at_rev_hub", + "status": "Received at Reverse Hub", + "remarks": "Received at hub Successfully", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:00:15Z", + "location": "SFX TestHub", + "status_id": "recd_at_fwd_hub", + "status": "Received at Forward Hub", + "remarks": "Item Received at pickup hub at SFX TestHub", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:00:45Z", + "location": "SFX TestHub", + "status_id": "item_manifested", + "status": "Item added to Bag", + "remarks": "Item added to bag at SFX TestHub", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:00:59Z", + "location": "SFX TestHub", + "status_id": "bag_in_transit", + "status": "Bag In Transit", + "remarks": "Bag in transit from SFX TestHub", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:01:26Z", + "location": "TEST DC", + "status_id": "bag_received", + "status": "Bag Received", + "remarks": "Bag received at TEST DC", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:01:53Z", + "location": "TEST DC", + "status_id": "recd_at_fwd_dc", + "status": "Received at Forward DC", + "remarks": "Item Received at dc at TEST DC", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:02:30Z", + "location": "TEST DC", + "status_id": "item_manifested", + "status": "Item added to Bag", + "remarks": "Item added to bag at TEST DC", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:02:52Z", + "location": "TEST DC", + "status_id": "bag_in_transit", + "status": "Bag In Transit", + "remarks": "Bag in transit from TEST DC", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:03:15Z", + "location": "TestHub2", + "status_id": "bag_received", + "status": "Bag Received", + "remarks": "Bag received at TestHub2", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:03:24Z", + "location": "TestHub2", + "status_id": "recd_at_fwd_hub", + "status": "Received at Forward Hub", + "remarks": "Item Received at hub at TestHub2", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:03:36Z", + "location": "TestHub2", + "status_id": "assigned_for_delivery", + "status": "Assigned For Delivery", + "remarks": "Item Assigned at TestHub2", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:03:54Z", + "location": "TestHub2", + "status_id": "ofd", + "status": "Out For Delivery", + "remarks": "Item OFD at TestHub2", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:04:09Z", + "location": "TestHub2", + "status_id": "cancelled_by_customer", + "status": "Cancelled", + "remarks": "Cancelled by customer", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:04:43Z", + "location": "TestHub2", + "status_id": "rts", + "status": "Return to Seller initiated", + "remarks": "Item Return To Seller at TestHub2", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:05:11Z", + "location": "TestHub2", + "status_id": "item_manifested", + "status": "Item added to Bag", + "remarks": "Item added to bag at TestHub2", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:05:24Z", + "location": "TestHub2", + "status_id": "in_transit_return", + "status": "In Transit for Return", + "remarks": "Bag in transit for return at TestHub2", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:05:54Z", + "location": "TEST DC", + "status_id": "bag_received", + "status": "Bag Received", + "remarks": "Bag received at TEST DC", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T09:06:15Z", + "location": "TEST DC", + "status_id": "recd_at_dc_rts", + "status": "Recived at RTS DC", + "remarks": "Item Received at RTS DC at TEST DC", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T10:01:29Z", + "location": "SFX TestHub", + "status_id": "rts_ofd", + "status": "Out for Delivery for RTS", + "remarks": "Item out for delivery to seller at SFX TestHub", + "awb_number": "SF222344412TST" + }, + { + "created": "2022-02-28T10:02:38Z", + "location": "SFX TestHub", + "status_id": "rts_nd", + "status": "Undelivered", + "remarks": "Item Return to Seller Not Delivered at SFX TestHub", + "awb_number": "SF222344412TST" + } + ] +} + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 400 (application/json) + + { + "message" : "Invalid AWB number" + } + + +## Multiple Order Details V4 Version [/v4/clients/bulk_track/] + +### Get Details of Multiple Delivery Requests [POST] + +This endpoint is used to get all details of multiple delivery requests, each defined by its AWB Number. The v4 version of tracking API provides the additional intermediate statuses like Item Manifested, Bag In Transit etc. The API can be used to track up to 50 AWBs in a single API call. + ++ Parameters + + format (optional, string) + Type of response (json or xml) required + + Default: json + ++ Request + + + Header + + Authorization: Token + + + Body + + { + "awb_numbers": [ + "SF1360594400TES", + "SF1360594301TES", + "SF1360593963TES" + ] +} + + + Attributes + + + awb_numbers (required, array) - An array of AWB numbers + + ++ Response 200 (application/json) + + { + "message": "Success", + "data": [ + { + "id": 852558400, + "client_name": "Test", + "client_order_id": "SPCH241063215", + "awb_number": "SF1360593963TES", + "product_value": 360.05, + "cod_amount": 0, + "payment_mode": "prepaid", + "order_date": "2024-08-27", + "promised_delivery_date": null, + "status_display": "Delivered to customer", + "status": "delivered", + "pickup_details": { + "name": "", + "contact": "", + "address_line_1": "", + "address_line_2": "", + "city": "Bangalore", + "state": "Karnataka", + "pincode": 562106, + "alternate_contact": "", + "sms_contact": "", + "latitude": null, + "longitude": null + }, + "delivery_details": { + "name": "", + "contact": "", + "address_line_1": "", + "address_line_2": "", + "city": "Bengaluru", + "state": "Karnataka", + "pincode": 560064, + "alternate_contact": "", + "sms_contact": "", + "latitude": null, + "longitude": null + }, + "product_details": [ + { + "sku_id": "8905040195551", + "sku_name": "", + "price": "", + "return_reason": null, + "category": "", + "brand": null, + "additional_details": "{}", + "seller_name": "BRAND STUDIO LIFESTYLE PVT LTD", + "seller_address": "Sy No. 330/B 324 331 331/B 331/1 and 334/1 Marasur Village Kasaba Hobli Anekal Taluk Bangalore Bengaluru KA IN 562106", + "seller_state": "Karnataka", + "hsn_code": "", + "invoice_no": "BSCH24108929", + "sgst_amount": 0.0, + "cgst_amount": 0.0, + "igst_amount": 0.0, + "gstin_number": "29AAGCB1468A1ZX", + "total_tax_value": 0.0, + "qc_required": true, + "qc_rules": "" + } + ], + "tracking_details": [ + { + "created": "2024-08-27T11:29:31Z", + "location": "Bangalore DC", + "status_id": "new", + "status": "New", + "remarks": "Item New at Bangalore DC", + "awb_number": "SF1360593963TES" + }, + { + "created": "2024-08-28T04:16:31Z", + "location": "Bangalore DC", + "status_id": "received_from_client_warehouse", + "status": "Received from Client Warehouse", + "remarks": "Item Received from Client Warehouse at Bangalore DC", + "awb_number": "SF1360593963TES" + }, + { + "created": "2024-08-28T08:27:10Z", + "location": "Bangalore DC", + "status_id": "recd_at_fwd_dc", + "status": "Received at Forward DC", + "remarks": "Item Received at dc at Bangalore DC", + "awb_number": "SF1360593963TES" + }, + { + "created": "2024-08-28T08:34:52Z", + "location": "Bangalore DC", + "status_id": "item_manifested", + "status": "Item added to Bag", + "remarks": "Item added to bag at Bangalore DC", + "awb_number": "SF1360593963TES" + }, + { + "created": "2024-08-28T09:48:40Z", + "location": "Bangalore DC", + "status_id": "bag_in_transit", + "status": "Bag In Transit", + "remarks": "Bag in transit from Bangalore DC", + "awb_number": "SF1360593963TES" + }, + { + "created": "2024-08-28T12:05:17Z", + "location": "BLR_ Yalahanka", + "status_id": "bag_received", + "status": "Bag Received", + "remarks": "Bag received at BLR_ Yalahanka", + "awb_number": "SF1360593963TES" + }, + { + "created": "2024-08-28T12:36:00Z", + "location": "BLR_ Yalahanka", + "status_id": "recd_at_fwd_hub", + "status": "Received at Forward Hub", + "remarks": "Item Received at hub at BLR_ Yalahanka", + "awb_number": "SF1360593963TES" + }, + { + "created": "2024-08-28T12:51:58Z", + "location": "BLR_ Yalahanka", + "status_id": "assigned_for_delivery", + "status": "Assigned For Delivery", + "remarks": "Item Assigned at BLR_ Yalahanka", + "awb_number": "SF1360593963TES" + }, + { + "created": "2024-08-28T12:52:57Z", + "location": "BLR_ Yalahanka", + "status_id": "ofd", + "status": "Out For Delivery", + "remarks": "Item OFD at BLR_ Yalahanka", + "awb_number": "SF1360593963TES" + }, + { + "created": "2024-08-28T14:31:39Z", + "location": "BLR_ Yalahanka", + "status_id": "delivered", + "status": "Delivered", + "remarks": "Item Delivered at BLR_ Yalahanka", + "awb_number": "SF1360593963TES" + } + ] + }, + { + "id": 852560471, + "client_name": "Test", + "client_order_id": "SPKH241810077", + "awb_number": "SF1360594301TES", + "product_value": 469.0, + "cod_amount": 574, + "payment_mode": "cod", + "order_date": "2024-08-27", + "promised_delivery_date": null, + "status_display": "Delivered to customer", + "status": "delivered", + "pickup_details": { + "name": "", + "contact": "", + "address_line_1": "", + "address_line_2": "", + "city": "Bangalore", + "state": "Karnataka", + "pincode": 562106, + "alternate_contact": "", + "sms_contact": "", + "latitude": null, + "longitude": null + }, + "delivery_details": { + "name": "", + "contact": "", + "address_line_1": "", + "address_line_2": "", + "city": "BANGALORE", + "state": "Karnataka", + "pincode": 560040, + "alternate_contact": "", + "sms_contact": "", + "latitude": null, + "longitude": null + }, + "product_details": [ + { + "sku_id": "8905745583530", + "sku_name": "", + "price": "", + "return_reason": null, + "category": "", + "brand": null, + "additional_details": "{}", + "seller_name": "BRAND STUDIO LIFESTYLE PVT.LTD", + "seller_address": "BRAND STUDIO LIFESTYLE PVT.LTD Block No. 1 Sy.No.18 Bandk Logistics ParkKasaba Hobli Kumbarnahalli Bangalore South", + "seller_state": "Karnataka", + "hsn_code": "", + "invoice_no": "BSKH24297919", + "sgst_amount": 0.0, + "cgst_amount": 0.0, + "igst_amount": 0.0, + "gstin_number": "29AAGCB1468A1ZX", + "total_tax_value": 0.0, + "qc_required": true, + "qc_rules": "" + } + ], + "tracking_details": [ + { + "created": "2024-08-27T11:31:25Z", + "location": "Bangalore DC", + "status_id": "new", + "status": "New", + "remarks": "Item New at Bangalore DC", + "awb_number": "SF1360594301TES" + }, + { + "created": "2024-08-28T04:22:00Z", + "location": "Bangalore DC", + "status_id": "received_from_client_warehouse", + "status": "Received from Client Warehouse", + "remarks": "Item Received from Client Warehouse at Bangalore DC", + "awb_number": "SF1360594301TES" + }, + { + "created": "2024-08-28T08:26:09Z", + "location": "Bangalore DC", + "status_id": "recd_at_fwd_dc", + "status": "Received at Forward DC", + "remarks": "Item Received at dc at Bangalore DC", + "awb_number": "SF1360594301TES" + }, + { + "created": "2024-08-28T08:31:13Z", + "location": "Bangalore DC", + "status_id": "item_manifested", + "status": "Item added to Bag", + "remarks": "Item added to bag at Bangalore DC", + "awb_number": "SF1360594301TES" + }, + { + "created": "2024-08-28T09:06:44Z", + "location": "Bangalore DC", + "status_id": "bag_in_transit", + "status": "Bag In Transit", + "remarks": "Bag in transit from Bangalore DC", + "awb_number": "SF1360594301TES" + }, + { + "created": "2024-08-28T11:46:01Z", + "location": "BLR_Magadiroad", + "status_id": "bag_received", + "status": "Bag Received", + "remarks": "Bag received at BLR_Magadiroad", + "awb_number": "SF1360594301TES" + }, + { + "created": "2024-08-28T11:58:00Z", + "location": "BLR_Magadiroad", + "status_id": "recd_at_fwd_hub", + "status": "Received at Forward Hub", + "remarks": "Item Received at hub at BLR_Magadiroad", + "awb_number": "SF1360594301TES" + }, + { + "created": "2024-08-28T12:46:55Z", + "location": "BLR_Magadiroad", + "status_id": "assigned_for_delivery", + "status": "Assigned For Delivery", + "remarks": "Item Assigned at BLR_Magadiroad", + "awb_number": "SF1360594301TES" + }, + { + "created": "2024-08-28T12:50:28Z", + "location": "BLR_Magadiroad", + "status_id": "ofd", + "status": "Out For Delivery", + "remarks": "Item OFD at BLR_Magadiroad", + "awb_number": "SF1360594301TES" + }, + { + "created": "2024-08-28T13:12:53Z", + "location": "BLR_Magadiroad", + "status_id": "delivered", + "status": "Delivered", + "remarks": "Item Delivered at BLR_Magadiroad", + "awb_number": "SF1360594301TES" + } + ] + }, + { + "id": 852560747, + "client_name": "Test", + "client_order_id": "SPKH241810054", + "awb_number": "SF1360594400TES", + "product_value": 569.05, + "cod_amount": 0, + "payment_mode": "prepaid", + "order_date": "2024-08-27", + "promised_delivery_date": null, + "status_display": "Delivered to customer", + "status": "delivered", + "pickup_details": { + "name": "", + "contact": "", + "address_line_1": "", + "address_line_2": "", + "city": "Bangalore", + "state": "Karnataka", + "pincode": 562106, + "alternate_contact": "", + "sms_contact": "", + "latitude": null, + "longitude": null + }, + "delivery_details": { + "name": "", + "contact": "", + "address_line_1": "", + "address_line_2": "", + "city": "BANGALORE", + "state": "Karnataka", + "pincode": 560076, + "alternate_contact": "", + "sms_contact": "", + "latitude": null, + "longitude": null + }, + "product_details": [ + { + "sku_id": "8909107428927", + "sku_name": "", + "price": "", + "return_reason": null, + "category": "", + "brand": null, + "additional_details": "{}", + "seller_name": "BRAND STUDIO LIFESTYLE PVT.LTD", + "seller_address": "BRAND STUDIO LIFESTYLE PVT.LTD Block No. 1 Sy.No.18 Bandk Logistics ParkKasaba Hobli Kumbarnahalli Bangalore South", + "seller_state": "Karnataka", + "hsn_code": "", + "invoice_no": "BSKH24297923", + "sgst_amount": 0.0, + "cgst_amount": 0.0, + "igst_amount": 0.0, + "gstin_number": "29AAGCB1468A1ZX", + "total_tax_value": 0.0, + "qc_required": true, + "qc_rules": "" + } + ], + "tracking_details": [ + { + "created": "2024-08-27T11:32:01Z", + "location": "Bangalore DC", + "status_id": "new", + "status": "New", + "remarks": "Item New at Bangalore DC", + "awb_number": "SF1360594400TES" + }, + { + "created": "2024-08-28T04:38:02Z", + "location": "Bangalore DC", + "status_id": "received_from_client_warehouse", + "status": "Received from Client Warehouse", + "remarks": "Item Received from Client Warehouse at Bangalore DC", + "awb_number": "SF1360594400TES" + }, + { + "created": "2024-08-28T08:26:48Z", + "location": "Bangalore DC", + "status_id": "recd_at_fwd_dc", + "status": "Received at Forward DC", + "remarks": "Item Received at dc at Bangalore DC", + "awb_number": "SF1360594400TES" + }, + { + "created": "2024-08-28T08:35:45Z", + "location": "Bangalore DC", + "status_id": "item_manifested", + "status": "Item added to Bag", + "remarks": "Item added to bag at Bangalore DC", + "awb_number": "SF1360594400TES" + }, + { + "created": "2024-08-28T09:36:29Z", + "location": "Bangalore DC", + "status_id": "bag_in_transit", + "status": "Bag In Transit", + "remarks": "Bag in transit from Bangalore DC", + "awb_number": "SF1360594400TES" + }, + { + "created": "2024-08-28T12:02:48Z", + "location": "BLR_Bannerghatta", + "status_id": "bag_received", + "status": "Bag Received", + "remarks": "Bag received at BLR_Bannerghatta", + "awb_number": "SF1360594400TES" + }, + { + "created": "2024-08-28T12:22:11Z", + "location": "BLR_Bannerghatta", + "status_id": "recd_at_fwd_hub", + "status": "Received at Forward Hub", + "remarks": "Item Received at hub at BLR_Bannerghatta", + "awb_number": "SF1360594400TES" + }, + { + "created": "2024-08-28T12:58:24Z", + "location": "BLR_Bannerghatta", + "status_id": "assigned_for_delivery", + "status": "Assigned For Delivery", + "remarks": "Item Assigned at BLR_Bannerghatta", + "awb_number": "SF1360594400TES" + }, + { + "created": "2024-08-28T12:58:31Z", + "location": "BLR_Bannerghatta", + "status_id": "ofd", + "status": "Out For Delivery", + "remarks": "Item OFD at BLR_Bannerghatta", + "awb_number": "SF1360594400TES" + }, + { + "created": "2024-08-28T14:09:41Z", + "location": "BLR_Bannerghatta", + "status_id": "delivered", + "status": "Delivered", + "remarks": "Item Delivered at BLR_Bannerghatta", + "awb_number": "SF1360594400TES" + } + ] + } + ] +} + + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 400 (application/json) + + { + "message" : "Invalid AWB Number" + } + ++ Response 400 (application/json) + { + "message": "Number of AWBs exceeded. Max count allowed is 50" + } + + +# group Update Order Details + +## Update Order Data [/v3/clients/order_update/] + +### Request to Update Order Data [POST] +The endpoint allows you to edit delivery_details, pickup_details,return_details and order status for an order. + +The API functionalities are as follows: + +* This API can be used to update the order status as `rts` / `rto` / `reopen_ndr` only if the current status of the order is either `on_hold` or `cancelled_by_customer` +* The API can be used to update customer details along with the Pincode, but the Pincode can only be updated twice per order and only for intra-city orders. +* The mandatory/required attributes should be passed as `null` if they do not need to be updated. +* The Pincode cannot be updated when the current status of the order is `delivered` . +* This API can be used to update the payment mode of the order to prepaid if the `cod_amount` is passed as 0, but the updation can only be done after the pickup phase i.e post the item is picked. +* The shipment weight can be updated only once, and only while the shipment is in new status. +* The delivery_details can be updated before OFD, the return_details can be edited until the order is cancelled_by_customer and pickup_details can be edited until the order is assigned_for_seller_pickup. + +

The `Authorization` header that should be used while sending request will look like: +
+`Token ` +
The `Authorization` value itself contains the Word “Token”. + +##Requests +This is the URL to make an actual request:
+||| +|------------|-----------| +|Staging|`https://dale.staging.shadowfax.in/api/v3/clients/order_update/ `| +|Production|`https://dale.shadowfax.in/api/v3/clients/order_update/ `| + ++ Request + + + Attributes + + + awb_numbers (required) - Awb number of an order needs to be passed. + + delivery_details - Pass the customer/ delivery details + + contact - Pass customer contact. + + alternate_contact - Pass customer contact. + + customer_address - Pass customer address. + + pincode - Pass customer/delivery pincode. + + latitude - Pass customer/delivery latitude. + + longitude - Pass customer/delivery longitude. + + pickup_details - Pass the pickup details + + contact - Pass pickup contact. + + customer_address - Pass pickup address. + + pincode - Pass pickup pincode. + + latitude - Pass pickup latitude. + + longitude - Pass pickup longitude. + + return_details - Pass the return details + + contact - Pass return contact. + + return_address - Pass return address. + + pincode - Pass return pincode. + + latitude - Pass return latitude. + + longitude - Pass return longitude. + + order_details - Pass order details. + + cod_amount (number) - Pass the cod amount that needs to be updated. + + eway_bill_number - Pass the e-way bill number for the respective awb number. + + return_eway_bill_number - Pass the return e-way bill number for the respective awb number once the shipment is in RTO status. + + invoice_number - Pass the invoice number. + + actual_weight - Weight of the Product (in gms) + + volumetric_weight - Volumetric Weight of the Product. default : 0.0 (volumetric weight=(lbh)/5000) + + status_update + + status -Pass the status that needs to be updated. The status values should be `rts` / `rto` / `reopen_ndr`. + + + + + + Body + + { + "awb_number": "SF47850936san", + "delivery_details": { + "contact": "9999999999", + "latitude":"77.6174", + "longitude": "12.9226", + "alternate_contact": "+919999999999", + "customer_address": "#253, 2nd cross hebbal", + "pincode": 560008 + }, + "pickup_details": { + "contact": "9123456781", + "alternate_contact": "9213456780", + "latitude": null, + "longitude": null, + "customer_address": "pickupaddress verkvj lrvnkjt", + "pincode": "560007" + }, + "return_details": { + "contact": "9123456781", + "latitude": "89.09", + "longitude": "67.09", + "email": "abc@gmail.com", + "capture_delivery_image": true, + "return_address": "abcd", + "pincode": "560007" + }, + "order_details": { + "cod_amount": 0, + "eway_bill_number": "xyz", + "invoice_number": "abc", + "return_eway_bill_number": "def", + "actual_weight": "539.127", + "volumetric_weight": "1498.314" + }, + "status_update": { + "status": null + } + } + + + Headers + + Authorization: Token + Content-Type: application/json + ++ Response 200 (application/json) + + { + "message": "Request accepted." + } + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 401 (application/json) + + { + "detail": "Invalid token." + } + ++ Response 400 (application/json) + + { + "message": "Status transition from recd_at_fwd_hub to ndr is not allowed" + } + ++ Response 400 (application/json) + + { + "message": "{\"message\":\"SF36981974TED pincode has been updated 2 times. The Pincode request is stored, will be validated and updated.\"}" + } + ++ Response 400 (application/json) + + { + "message": "{\"message\":\"Intercity pincode updates are not allowed\"}" + } + ++ Response 400 (application/json) + + { + "message": "Customer details could not be updated for SF36981985TED due to invalid status. Current status is Delivered to customer" + } + ++ Response 400 (application/json) + + { + "message": "Customer contact number can not be blank" + } + + +#group Order Cancellation +## Order Cancellation [/v3/clients/orders/cancel/] + +### Cancel a Request [POST] + +This endpoint cancels marketplace or warehouse orders using the AWB Number or unique client order id, along with the cancellation reason. + +For successful cancellation, we give a Response message as “Request has been marked as cancelled” & 200 as response code. + +For below mentioned statues, the API queues the cancellation request, returning 'Request is queued for cancellation' & 304 as response code. +* Out for pickup (ofp) +* received at reverse hub (recd_at_rev_hub) +* Out for delivery (ofd) +* Bag In Transit (bag_in_transit) +* Bag Received at Via (bag_received_at_via) + +The cancellation is executed when the shipments are received in the next SFX facility. In case the shipment is “Delivered”, then the canceling request is not executed. + +Cancellation after delivery results in failure, returning 'Order cannot be cancelled. Invalid state.' & 400 as response code. + ++ Request + + + Body + + { + "request_id": "SF10000002NRN", + "cancel_remarks": "Request cancelled by customer" + } + + + Headers + + Authorization: Token Authorization + + + Attributes + + + Required Parameters(required, object) + + request_id(required, string) - AWB number of the order or the client order id of the order. There should be a unique client order id for each order. + + cancel_remarks(required , string) - Valid options are discussed in the description + ++ Response 200 (application/json) + + { + "responseMsg": "Request has been marked as cancelled", + "responseCode": 200 + } + ++ Response 200 (application/json) + + { + "responseMsg": "Request is queued for cancellation.", + "responseCode": 304 + } + ++ Response 200 (application/json) + + { + "responseMsg": "The request is already in its cancellation phase", + "responseCode": 200 + } + ++ Response 401 (application/json) + + { + "status": "FAILED", + "errorCode": "Authentication credentials not provided", + "message": "Authentication credentials not provided" + } + ++ Response 400 (application/json) + + { + "responseMsg": "Please enter a valid AWB Number", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Order cannot be cancelled. Invalid state.", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Multiple Orders found for the given Request Id", + "responseCode": 400 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Unable to cancel the request. Please try again!", + "responseCode": 500 + } + ++ Response 400 (application/json) + + { + "responseMsg": "Cannot cancel order from Pincode Updated", + "responseCode": 400 + } + + +#group Escalation API +## Escalation API [/v1/clients/support/issue/] +### Escalation API [POST] +This endpoint is used take escalations from clients in real time. Based on the issue category, a ticket would be raised in the system. This API is to take the escalation input from the clients. The action being taken on those tickets . + +####Issue Category options + +|Issue Category |Issue Category Code |Validations | Order Type | +|--------------------------------------|---------------------|----------------------------------------------------------------------------------------------------------------|---------------------| +| `Delayed Delivery` | 1 |To be passed post an order is successfully picked in system and not delivered post to Estimated Delivery Date | Forward | +| `Expedite Pickup - Customer` | 2 |To be passed only if the order is not yet picked from the Customer | Reverse | +| `Expedite Pickup - Seller` | 3 |To be passed only until the seller pickup is not completed | Forward | +| `Status Mismatch` | 4 |To be passed when there is a mismatch in expected and received order status | Forward/Reverse | +| `Delivery Dispute` | 5 |To be passed only post the order is marked Delivered in the system | Forward | + + ++ Request + + Body + { + "awb_number" : "SF00000000TC", + "issue_category" : 1 + } + + + + + Headers + Authorization: Token Authorization + + Attributes + + Required Parameters(required, object) + + awb_number(required, string) - It should be a valid Awb number. + + issue_category(required , number) - Issue category will be one among choices given in Issue Category options . + ++ Response 200 (application/json) + + { + "message": "Issue received", + "status": "SUCCESS" + } + + + +#group Get POD Details + +## Get POD Details [/v1/clients/pod_details/] + +### Get POD Details[POST] + +Using this API, you can get POD (proof of delivery) details for multiple delivery requests, each defined by their AWB number. +This API will allow you to get pod details for up to 100 AWB numbers. +`To obtain POD details, the order status of the AWB_number's passed must be delivered or rts_d` + ++ Request (application/json) + + + Headers + + Authorization: Token 1234567890abc1234567890xyz + + + + Body + + { + "awb_numbers": [ + "SF17102220TE", + "SF17102219TE", + "SF16330181TE" + ] + } + ++ Response 200 (application/json) + + + { + "message": "Success", + "pod_details": { + "SF16330181TE": { + "recipient": null, + "recipient_contact": "None", + "recipient_signature": "['https://s3-us-west-2.amazonaws.com/sfxdocs/seller/delivery_report/report_1992_1636536053.pdf', 'https://s3-us-west-2.amazonaws.com/sfxdocs/seller/delivery_report/report_1992_1636536184.pdf']", + "recipient_name": "None" + }, + "SF17102219TE": { + "recipient": "CUSTOMER", + "recipient_contact": "None", + "recipient_signature": "None", + "recipient_name": "Deepak Rastogi" + }, + "SF17102220TE": { + "recipient": "CUSTOMER", + "recipient_contact": "None", + "recipient_signature": "None", + "recipient_name": "Deepak Rastogi" + } + } + } + ++ Response 400 (application/json) + + { + "message": "AWB does not belong to this client" + } + ++ Response 400 (application/json) + + { + "message": "Incorrect AWB" + } + ++ Response 400 (application/json) + + { + "message": "Max limit on count is 100. Please send a list within 100 AWBs" + } + ++ Response 400 (application/json) + + { + "message": "Incorrect Status" + } + ++ Response 401 (application/json) + + { + "detail": "Invalid token." + } + + + +# Group Generate QR Code +## Generate QR Code v2 Version [POST] +This API would help you to generate the QR code on invoices and labels for B2C shipments as per the guidelines of the Government of India. Using this API, Clients can get the QR code after the order has been placed successfully in the Shadowfax system. API would return the respective QR string generated with all the parameters passed in the request which the client would use to generate QR code and print it on the invoices. Clients can show the same QR code on +the invoice/shipment labels and payment can be made by this QR code by the end customer. + +## Requests +This is the URL to make an actual request:
+||| +|------------|-----------| +|Staging|`https://saruman.staging.shadowfax.in/api/v2/clients/qr_code/generate/`| +|Production|`https://saruman.shadowfax.in/api/v2/clients/qr_code/generate/`| + + + ++ Request (application/json) + + + Body + + { + "awb_number": "SF29534985888TER", + "gstin": "36AAVCS6697K1Z4", + "invoice_number": "ADSf", + "invoice_date": "2021-03-25 12:20:16", + "amount":1.00, + "taxableValue":12.00, + "gstPercentage":12.00, + "gst": 23.00, + "cgst": 0.00, + "sgst": 23.00, + "igst": 23.00, + "cess": 0.00 + } + + + + Headers + + Authorization: Token + + + Attributes + + + Required Parameters(required, object) + + awb_number(required, string) - AWB Number of the shipment + + gstin(required , string) - Seller GSTIN + + invoice_number(required, string) - Invoice Number for the Shipment + + invoice_date(required, string) - Invoice Date + + amount(required, string) - Pass the cash amount to be collected for which payment has to be made. + + taxableValue(required, string) - Total invoice value (declared value) of the shipment. + + gstPercentage(required, string) - GST Tax slab rate e.g. 12% / 18% in the format of number i.e. 18 or 12 or 24 + + gst(required, string) - Total GST tax amount charged. Should be equal to INVOICE_AMOUNT * GST_RATE + + cgst(required, string) - CGST portion of total GST amount. Can be 0 or greater than 0 + + igst(required, string) - IGST portion of total GST amount. Can be 0 or greater than 0 + + sgst(required, string) - SGST portion of total GST amount. Can be 0 or greater than 0 + + cess(required, string) - Cess amount if any. Can be 0 or greater than 0 + + + + ++ Response 200 (application/json) + + { + "message": "success", + "payment_mode": "UPI/QR", + "qr_details": { + "qr_code_string": "upi://pay?pa=BHARATPE30029095568@yesbankltd&pn=Shadowfax+Technologies+Private+Limited&cu=INR&tn=Pay+To+Shadowfax+Technologies+Private+Limited&tr=BPD29095568&am=1.0&awb_number=SF29534985888TER&taxableValue=12.0&gstin=36AAVCS6697K1Z4&gstPercentage=12.0&gstBreakup=gst:23.0|cgst:0.0|sgst:23.0|igst:23.0|cess:0.0&invoice_number=ADSf&invoice_date=2021-03-25 12:20:16", + "transaction_id": "BHARATPE30029095568" + } + } + ++ Response 403 (application/json) + + { + "detail": "You do not have permission to perform this action." + } + ++ Response 400 (application/json) + + { + "message": { + "awb_number": [ + "This field may not be blank." + ] + } + } + ++ Response 400 (application/json) + + { + "message": { + "awb_number": [ + "This field is required." + ] + } + } + ++ Response 400 (application/json) + + { + "message": "Only Cash payments are enabled for this order." + } + ++ Response 400 (application/json) + + { + "message": { + "amount": [ + "Invalid transaction due to negative pay amount." + ] + } + } + ++ Response 400 (application/json) + + { + "message": "Invalid AWB." + } + ++ Response 400 (application/json) + + { + "message": "Invalid Client." + } + ++ Response 400 (application/json) + + { + "message": "QR Code can only be generated for customer delivery." + } + ++ Response 400 (application/json) + + { + "message": "QR Code can only be generated for COD." + } + ++ Response 400 (application/json) + + { + "message": "Failed to generate qr code for delivery_request." + } + ++ Response 400 (application/json) + + { + "message": "Amount entered does not match with COD amount" + } + + +# Group Issues and Bugs +Please drop in a mail to to report any bug(s) in the Client APIs. Any issue with respect to integration +can also be raised on this forum. Our team shall review the issue and get back to you in due time. + + Client Queries Troubleshooter - https://docs.google.com/spreadsheets/d/1c7EfdwrxQrqz9zhwaa4bE0dI0EI41PT5ynY74aDJ7S8/edit#gid=1815509323 + +# Group Appendix + +## On Hold Remarks for marketplace + +Following are the remarks shared in the cases when the order is marked as `On Hold`. + +| Status | Remarks | +|---------|------------------------------------| +| On Hold | Incorrect/incomplete contact info | +| On Hold | Pincode/address mismatch | +| On Hold | Customer wants open delivery | +| On Hold | Customer shifted from given address | +| On Hold | Non Serviceable Area | +| On Hold | Customer wants delivery on another address | +| On Hold | High Volume Shipment | +| On Hold | Three succesful attempts done | +| On Hold | Customer wants delivery beyond cut-off date | +| On Hold | Payment Issue | +| On Hold | High Ageing | +| On Hold | Successful attempts done | +| On Hold | COVID Restricted Area | +| On Hold | Same City Pincode Update | +| On Hold | InterCity Shipment | +| On Hold | Address is Junk | +| On Hold | RTO Pending - Max attempts exhausted | +| On Hold | RTO Pending - OTP validated cancellation | +| On Hold | RTO Pending - OTP not validated cancellation | +| On Hold | RTO Pending - Cancelled by customer through webapp | +| On Hold | RTO Pending - Awaiting runsheet closure | + + +## Pickup On Hold Remarks for marketplace + +Following are the remarks shared in the cases when the order is marked as `pickup_on_hold`. + +| Status | Remarks | +|---------|------------------------------------| +| On Hold | Customer want pick-up from Non-Serviceable area | +| On Hold | Pincode Address Mismatch | +| On Hold | Incorrect contact number | +| On Hold | Address issue | +| On Hold | Customer wants replacement/refund first | +| On Hold | Incomplete information received from client | +| On Hold | Mandatory check not available | +| On Hold | Pickup already done by other DSP | +| On Hold | Shipment is not picked | +| On Hold | Successful 3 attempts done | +| On Hold | Large Shipment | +| On Hold | Customer wants pickup beyond cut off time | +| On Hold | Customer changed his/her mind | +| On Hold | Doorstep QC Brandbox not available | +| On Hold | DOORSTEP_QC_PRICE_TAG_MISSING | +| On Hold | DOORSTEP_QC_PRODUCT_DAMAGE | +| On Hold | DOORSTEP_QC_PRODUCT_MISMATCH | +| On Hold | DOORSTEP_QC_USED_ITEM | +| On Hold | Unable to pickup in given slot | +| On Hold | Non Serviceable Area | +| On Hold | Seller wants pickup beyond cut off time | +| On Hold | COVID Restricted Area | + +## Pickup On Hold Remarks for marketplace PUSH API + +Following are the remarks shared in the cases when the order is marked as `pickup_on_hold`. + +| Status | Remarks | +|---------|------------------------------------| +| On Hold | Customer want pick-up from Non-Serviceable area | +| On Hold | Pincode Address Mismatch | +| On Hold | Incorrect contact number | +| On Hold | Address issue | +| On Hold | Customer wants replacement/refund first | +| On Hold | Incomplete information received from client | +| On Hold | Mandatory check not available | +| On Hold | Pickup already done by other DSP | +| On Hold | Shipment is not picked | +| On Hold | Successful 3 attempts done | +| On Hold | Large Shipment | +| On Hold | Customer wants pickup beyond cut off time | +| On Hold | Customer changed his/her mind | +| On Hold | Doorstep QC Brandbox not available | +| On Hold | DOORSTEP_QC_PRICE_TAG_MISSING | +| On Hold | DOORSTEP_QC_PRODUCT_DAMAGE | +| On Hold | DOORSTEP_QC_PRODUCT_MISMATCH | +| On Hold | DOORSTEP_QC_USED_ITEM | +| On Hold | Unable to pickup in given slot | +| On Hold | Non Serviceable Area | +| On Hold | Seller wants pickup beyond cut off time | +| On Hold | COVID Restricted Area | + + +## Not Contactable Remarks for marketplace +Following are the remarks shared in the cases when the order is marked as `nc`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Not Contactable | Customer not contactable | +| Not Contactable | Residence or Office Closed | + + +## Not Contactable Remarks for marketplace PUSH API +Following are the remarks shared in the cases when the order is marked as `nc`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Not Contactable | Customer not contactable | +| Not Contactable | Residence or Office Closed | + + +## Cancelled Remarks +Following are the remarks shared in the cases when the order is marked as `cancelled`. + +| Status | Remarks | +|-----------|------------------------------------| +| Cancelled | Cancelled by customer | +| Cancelled | Cancelled by Client | +| Cancelled | Third Successful Attempt | +| Cancelled | Cancelled as per client's request | +| Cancelled | Central Ops Queued Return | + + +## Cancelled Remarks for Marketplace PUSH API +Following are the remarks shared in the cases when the order is marked as `cancelled`. + +| Status | Remarks | +|-----------|------------------------------------| +| Cancelled | Cancelled by customer | +| Cancelled | Cancelled by Client | +| Cancelled | Third Successful Attempt | +| Cancelled | Cancelled as per client's request | +| Cancelled | Central Ops Queued Return | + + +## Return to origin for warehouse PUSH API +Following are the remarks shared in the cases when the order is marked as `RTO`. + +| Status | Remarks | +|-----------|------------------------------------| +| RTO | NSZ/Junk/Different City/Long Distance Pincode | +| RTO | Client instructions for RTO | +| RTO | >= 3+ Attempt RTO | +| RTO | Exception | +| RTO | Misrouted/Pincode Updated| +| RTO | Validated by OTP | +| RTO | Max IVR attempts exhausted | +| RTO | Validated by IVR/Webapp | +| RTO | Cancelled by client over API| +| RTO | Marked return by cron due to inactivity| + +## Not Attempted Remarks +Following are the remarks shared in the cases when the order is marked as `not_attempted`. + +| Status | Remarks | +|---------------|------------------------------------| +| Not Attempted | Not Attempted | +| Not Attempted | Vehicle Breakdown | +| Not Attempted | Heavy Rain | + +## Not Attempted Remarks for Marketplace PUSH Api +Following are the remarks shared in the cases when the order is marked as `not_attempted`. + +| Status | Remarks | +|---------------|------------------------------------| +| Not Attempted | Not Attempted | +| Not Attempted | Vehicle Breakdown | +| Not Attempted | Heavy Rain | + +## On Hold Remarks for warehouse + +Following are the remarks shared in the cases when the order is marked as `On Hold`. + +| Status | Remarks | +|---------|------------------------------------| +| On Hold | Incorrect/incomplete contact info | +| On Hold | Pincode/address mismatch | +| On Hold | Customer wants open delivery | +| On Hold | Customer shifted from given address | +| On Hold | Non Serviceable Area | +| On Hold | Customer wants delivery on another address | +| On Hold | High Volume Shipment | +| On Hold | Three succesful attempts done | +| On Hold | Customer wants delivery beyond cut-off date | +| On Hold | Payment Issue | +| On Hold | High Ageing | +| On Hold | Successful attempts done | +| On Hold | COVID Restricted Area | +| On Hold | Eligible for liquidation | +| On Hold | Liquidated | +| On Hold | Same City Pincode Update | +| On Hold | InterCity Shipment | +| On Hold | Address is Junk | +| On Hold | RTO Pending - Max attempts exhausted | +| On Hold | RTO Pending - OTP validated cancellation | +| On Hold | RTO Pending - OTP not validated cancellation | +| On Hold | RTO Pending - Cancelled by customer through webapp | +| On Hold | RTO Pending - Awaiting runsheet closure | + + +## On Hold Remarks for Marketplace PUSH API + +Following are the remarks shared in the cases when the order is marked as `On Hold`. + +| Status | Remarks | +|---------|------------------------------------| +| On Hold | Incorrect/incomplete contact info | +| On Hold | Pincode/address mismatch | +| On Hold | Customer wants open delivery | +| On Hold | Customer shifted from given address | +| On Hold | Non Serviceable Area | +| On Hold | Customer wants delivery on another address | +| On Hold | High Volume Shipment | +| On Hold | Three succesful attempts done | +| On Hold | Customer wants delivery beyond cut-off date | +| On Hold | Payment Issue | +| On Hold | High Ageing | +| On Hold | Successful attempts done | +| On Hold | COVID Restricted Area | +| On Hold | Eligible for liquidation | +| On Hold | Liquidated | +| On Hold | Same City Pincode Update | +| On Hold | InterCity Shipment | +| On Hold | Address is Junk | +| On Hold | RTO Pending - Max attempts exhausted | +| On Hold | RTO Pending - OTP validated cancellation | +| On Hold | RTO Pending - OTP not validated cancellation | +| On Hold | RTO Pending - Cancelled by customer through webapp | +| On Hold | RTO Pending - Awaiting runsheet closure | + +## On Hold Remarks for Warehouse PUSH API + +Following are the remarks shared in the cases when the order is marked as `On Hold`. + +| Status | Remarks | +|---------|------------------------------------| +| On Hold | Incorrect/incomplete contact info | +| On Hold | Pincode/address mismatch | +| On Hold | Customer wants open delivery | +| On Hold | Customer shifted from given address | +| On Hold | Non Serviceable Area | +| On Hold | Customer wants delivery on another address | +| On Hold | High Volume Shipment | +| On Hold | Three succesful attempts done | +| On Hold | Customer wants delivery beyond cut-off date | +| On Hold | Payment Issue | +| On Hold | High Ageing | +| On Hold | Successful attempts done | +| On Hold | COVID Restricted Area | +| On Hold | Eligible for liquidation | +| On Hold | Liquidated | +| On Hold | Same City Pincode Update | +| On Hold | InterCity Shipment | +| On Hold | Address is Junk | +| On Hold | RTO Pending - Max attempts exhausted | +| On Hold | RTO Pending - OTP validated cancellation | +| On Hold | RTO Pending - OTP not validated cancellation | +| On Hold | RTO Pending - Cancelled by customer through webapp | +| On Hold | RTO Pending - Awaiting runsheet closure | + + +## Not Contactable Remarks +Following are the remarks shared in the cases when the order is marked as `nc`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Not Contactable | Customer not contactable | +| Not Contactable | Item Customer Not Contactable at | + + +## Not Contactable Remarks for Warehouse PUSH API +Following are the remarks shared in the cases when the order is marked as `nc`. + +| Status | Remarks | +|-----------------|------------------------------------| +| Not Contactable | Customer not contactable | +| Not Contactable | Item Customer Not Contactable at diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..b882352 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'shippingservice_project.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b62059b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +Django>=5.0,<7.0 +requests>=2.31.0 +psycopg2-binary>=2.9.0 +gunicorn>=21.0.0 diff --git a/shipping/__init__.py b/shipping/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/shipping/__pycache__/__init__.cpython-312.pyc b/shipping/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d61b591d0f0dbd6591938f2e84cb2d19526b7a43 GIT binary patch literal 154 zcmX@j%ge<81h1!V%L37lK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%ThlhKQ~psEHgbX zwKzjRJ+maEG)ccWBeS3&GcUb3wWusJITg;=kB`sH%PfhH*DI*}#bJ}1pHiBWYFESx UG>H+2i$RQ!%#4hTMa)1J0O`>t$p8QV literal 0 HcmV?d00001 diff --git a/shipping/__pycache__/admin.cpython-312.pyc b/shipping/__pycache__/admin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d45e81b813f8f903bba043bb10c15f7db201803e GIT binary patch literal 198 zcmX@j%ge<81h1!V%d!X3k3k$5V1hC}3ji6@8B!Qh7;_kM8KW2(87i4HnO}mWH5qTQ zCZ^vS_lt1aUMOZ*e*n6gcPSrDdikL!==jBb4)50LYlmkjfCnn8Fanl)@Or zT*;)#e2XtQBeS3&GcO&Y_ZCMnl<%j>a*HEAJ})shH9r0pSA2YKeoAQ$h|LopUs#%$ z1C?O`(JL7~gY5Zbs-Kaco2p-ynVy$goS~ndS&~tjqz^N)IJKxOGdUH`*H0`cDAp^e zECP8POt1hM#q2<$h2aLbXroIJ4@|}n=vMBOti-(Zd_9o1B2XYeG;)JjV1>Lu)-Mhl mh)eB?_<>xI|BFR{#0O?ZM#j4gieK0m8O0_Vk1tH32E5Nt&;U5<5W&xUwBkTqUc;JJ=@nu9;mE zu+r2A4(X+$mnKqFDe8$T95_|{36XlSRZF&3>ZvDgX-HLxQ{SwOYa$|LB)|E1zj^ca z_deivm#YiGaQ*b#Qpk$XUvzUYU~i7SOE5h}GLo4b$}>rZK`gp~WOg0N7R)ShEnLU+ z($d@{Eo+=$-K86WF>HBN#yKt30YvRb+$?$Qs!mX*0DB@ z|3I9}#0_*>YOdv;EqD7HdOpw^5DVN59bv7v!3?D1RPws*%r z&^y1GN{~k_^cmfgDLuQA)z@yTX~80}hCLFE%}>UzmwF>Ac_*qSUgcT=!QXZHGyRVuTsWJ^ea}Xy?P+`1O)KyVYABvJJAF8uf=A%6iApfs3nMp&H zt+9e--`&CV*bLUCQrB5Mo5z|izy*r*mPXSG_!b$WzE;3Sw*+~wDkkHGk6^8!DjF6c7Qv`i97LwJ)u!FoKqQzM6x(1lM#9~ zL=Sna$OSd4=wgxNjP8uC7sNc)GpcMj6L?iO@kS59%SFgsF@q(@N6p|54i;2-&ETgM z9j9^2_-Sg_)uz})bVYKi)H2qKS8=LnMuSo;<~76CjDz92F6lS{hfOzWQ#7oKs$3u|{Yb3`0=16bu-pk-Ir@g@{I>G!YaaQ68|UlZ;i`9dlk9w1 zN!+V>@0V{kJVP6yx@V~B8QM(MJm<={8s6bHP^)=AJB(S_3DrE4fbxen7VG{{)gRhS z?3}Clua%>nH-kHD&3hT{_K!U~U+*8Q_K$6Awf@T$uCD=}MC!g^)fe2H*zweSSIRSu zKH-_I-X~Q1gze#4-}wr6yx|&rGFNvER$YUep6&au?$%uM`RwAE@#JFDAAdp5?}${Ld)+I5`rlBA^7F?pex71kO0 zPehZ&!_4kGt~9&SHF6cGr4^vj#CvUF80Jqj@RmEpOtKrHr)PdV^ADnHGb;Z9xaK4$ literal 0 HcmV?d00001 diff --git a/shipping/__pycache__/sfx_client.cpython-312.pyc b/shipping/__pycache__/sfx_client.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d743a46d2a4e9dcefe0b98178f8a45440437cae GIT binary patch literal 3804 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 literal 0 HcmV?d00001 diff --git a/shipping/__pycache__/urls.cpython-312.pyc b/shipping/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67c7ff1f7787548bc58bebcd464a89c549bb51e1 GIT binary patch literal 1424 zcmZ{jzfaph6vxlRfrR`dBq1UE5JN?gAf^haEuw5qf2mbFu&`Jrca05>9i5#3*}7$< z6B+skP_=Xai7vKe$*Lumc47lDm8o}j4Ae3n-o5wr=e>J(_WPMijRW%h{_fD-@#WMt4{b{Gc05g3t2T9KUyLtY{VvM90_^Am4Z82vu_E#k#4H%2&j z$6JPaFc#qAgiHRtx>8d?n#eUJUF_^~nkco~lG4-=?n(`G6LYksYWwIqw%Jl)M@Cz? zOsde|N&8F8MTB9PGca=oCNE=&+jeTfOf47>pIGeEUu>*DeY|ZK7R|!qsbaCu z!j-Ax2PfQT?lfVsYlJZ3vxAO3Uo+=x#>Q)leM7Tm2T{AUVwP5nwdWRFr&8q@*~MkE zxNOvXX>L%k%Qdr165m_whs#mYl@Tuw*6qrwSy?q6zqHs_?iS0Z$gbASYTbA%SnTdS bTHh^LHOp0Fy>78DF6OhDkxvOcMNa<@c<5u* literal 0 HcmV?d00001 diff --git a/shipping/__pycache__/views.cpython-312.pyc b/shipping/__pycache__/views.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e29145db1b0c0c5171ff4fec11498d47b5e81401 GIT binary patch literal 31183 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 literal 0 HcmV?d00001 diff --git a/shipping/migrations/__pycache__/__init__.cpython-312.pyc b/shipping/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b01cc4dad409a12a4b8877fb823be6e3f3bf1398 GIT binary patch literal 165 zcmX@j%ge<81h1!V%L37lK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%SAsUKQ~psEHgbX zwKzjRJ+maEG)ccWBeS3&GcUb3wWusJITg;=&&^CPN-W9D&nwoCkI&4@EQycTE2#X% fVUwGmQks)$SHuc5lo5!FL5z>gjEsy$%s>_Z268Jv literal 0 HcmV?d00001 diff --git a/shipping/models.py b/shipping/models.py new file mode 100644 index 0000000..0d16325 --- /dev/null +++ b/shipping/models.py @@ -0,0 +1,33 @@ +from django.db import models + +class ActivityLog(models.Model): + SERVICE_CHOICES = [ + ('FORWARD', 'Forward Logistics'), + ('REVERSE', 'Reverse Logistics'), + ('WEBHOOK', 'Webhook Event'), + ] + + STATUS_CHOICES = [ + ('SUCCESS', 'Success (2xx)'), + ('ERROR', 'Error (4xx/5xx)'), + ('WARNING', 'Warning'), + ] + + service_type = models.CharField(max_length=20, choices=SERVICE_CHOICES, default='FORWARD') + action_name = models.CharField(max_length=100) + endpoint_url = models.CharField(max_length=500) + http_method = models.CharField(max_length=10, default='POST') + request_headers = models.TextField(blank=True, default='') + request_body = models.TextField(blank=True, default='') + response_status_code = models.IntegerField(default=200) + response_body = models.TextField(blank=True, default='') + execution_time_ms = models.FloatField(default=0.0) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='SUCCESS') + timestamp = models.DateTimeField(auto_now_add=True) + notes = models.TextField(blank=True, default='') + + class Meta: + ordering = ['-timestamp'] + + def __str__(self): + return f"[{self.service_type}] {self.action_name} - {self.response_status_code} ({self.timestamp.strftime('%H:%M:%S')})" diff --git a/shipping/sfx_client.py b/shipping/sfx_client.py new file mode 100644 index 0000000..cf412c0 --- /dev/null +++ b/shipping/sfx_client.py @@ -0,0 +1,81 @@ +import json +import time +import requests +from .models import ActivityLog + +STAGING_BASE_URL = "https://dale.staging.shadowfax.in" +PRODUCTION_BASE_URL = "https://flash.shadowfax.in" + +def get_base_url(env="staging"): + if env == "production": + return PRODUCTION_BASE_URL + return STAGING_BASE_URL + +def execute_sfx_call(service_type, action_name, method, full_url, headers, payload=None, env="staging"): + start_time = time.time() + response_status = 500 + response_body_str = "" + status_label = "ERROR" + + headers_dict = headers or {} + req_body_str = json.dumps(payload, indent=2) if isinstance(payload, (dict, list)) else (payload or "") + req_headers_str = json.dumps(headers_dict, indent=2) + + try: + if method.upper() == "GET": + resp = requests.get(full_url, headers=headers_dict, timeout=10) + elif method.upper() == "POST": + resp = requests.post(full_url, headers=headers_dict, json=payload if isinstance(payload, (dict, list)) else None, data=payload if isinstance(payload, str) else None, timeout=10) + elif method.upper() == "PUT": + resp = requests.put(full_url, headers=headers_dict, json=payload if isinstance(payload, (dict, list)) else None, data=payload if isinstance(payload, str) else None, timeout=10) + elif method.upper() == "DELETE": + resp = requests.delete(full_url, headers=headers_dict, timeout=10) + else: + resp = requests.request(method, full_url, headers=headers_dict, json=payload, timeout=10) + + response_status = resp.status_code + try: + parsed_json = resp.json() + response_body_str = json.dumps(parsed_json, indent=2) + except Exception: + response_body_str = resp.text + + if 200 <= response_status < 300: + status_label = "SUCCESS" + elif 400 <= response_status < 500: + status_label = "ERROR" + else: + status_label = "ERROR" + + except requests.exceptions.RequestException as e: + response_status = 502 + response_body_str = json.dumps({ + "error": "Upstream Connection Failed", + "details": str(e), + "note": "Shadowfax Staging API host may be unreachable directly from your machine or blocked by Akamai WAF. Simulation mode fallback can be enabled." + }, indent=2) + status_label = "ERROR" + + duration_ms = round((time.time() - start_time) * 1000, 2) + + # Save to ActivityLog DB model + log_entry = ActivityLog.objects.create( + service_type=service_type, + action_name=action_name, + endpoint_url=full_url, + http_method=method.upper(), + request_headers=req_headers_str, + request_body=req_body_str, + response_status_code=response_status, + response_body=response_body_str, + execution_time_ms=duration_ms, + status=status_label + ) + + return { + "log_id": log_entry.id, + "status_code": response_status, + "execution_time_ms": duration_ms, + "status": status_label, + "response": response_body_str + } diff --git a/shipping/static/css/style.css b/shipping/static/css/style.css new file mode 100644 index 0000000..3339925 --- /dev/null +++ b/shipping/static/css/style.css @@ -0,0 +1,294 @@ +:root { + --bg-dark: #0f172a; + --bg-card: #1e293b; + --bg-input: #0f172a; + --border-color: #334155; + --text-main: #f8fafc; + --text-muted: #94a3b8; + --accent-cyan: #06b6d4; + --accent-blue: #3b82f6; + --accent-purple: #8b5cf6; + --accent-pink: #ec4899; + --accent-green: #10b981; + --accent-amber: #f59e0b; + --accent-red: #ef4444; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Inter', system-ui, -apple-system, sans-serif; + background-color: var(--bg-dark); + color: var(--text-main); + line-height: 1.5; + min-height: 100vh; +} + +/* Navbar */ +.navbar { + background: rgba(30, 41, 59, 0.8); + backdrop-filter: blur(12px); + border-bottom: 1px solid var(--border-color); + padding: 1rem 2rem; + display: flex; + align-items: center; + justify-content: space-between; + position: sticky; + top: 0; + z-index: 100; +} + +.brand { + display: flex; + align-items: center; + gap: 0.75rem; + font-weight: 700; + font-size: 1.25rem; + background: linear-gradient(135deg, var(--accent-cyan), var(--accent-blue)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.nav-tabs { + display: flex; + gap: 0.5rem; +} + +.nav-link { + color: var(--text-muted); + text-decoration: none; + padding: 0.5rem 1.25rem; + border-radius: 0.5rem; + font-weight: 5rem; + font-size: 0.9rem; + transition: all 0.2s ease; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.nav-link:hover { + color: var(--text-main); + background: rgba(255, 255, 255, 0.05); +} + +.nav-link.active { + color: #fff; + background: linear-gradient(135deg, rgba(6, 182, 212, 0.2), rgba(59, 130, 246, 0.2)); + border: 1px solid rgba(6, 182, 212, 0.4); +} + +/* Layout Container */ +.container { + max-width: 1400px; + margin: 0 auto; + padding: 2rem; +} + +.page-header { + margin-bottom: 2rem; +} + +.page-title { + font-size: 1.75rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +.page-desc { + color: var(--text-muted); + font-size: 0.95rem; +} + +/* Grid & Cards */ +.grid-2 { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; +} + +@media (max-width: 1024px) { + .grid-2 { + grid-template-columns: 1fr; + } +} + +.card { + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: 0.75rem; + padding: 1.5rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); +} + +.card-title { + font-size: 1.1rem; + font-weight: 600; + margin-bottom: 1rem; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--border-color); + padding-bottom: 0.75rem; +} + +/* Form Controls */ +.form-group { + margin-bottom: 1rem; +} + +.form-label { + display: block; + font-size: 0.85rem; + font-weight: 500; + color: var(--text-muted); + margin-bottom: 0.35rem; +} + +.form-control, .form-select { + width: 100%; + background: var(--bg-input); + border: 1px solid var(--border-color); + border-radius: 0.5rem; + padding: 0.6rem 0.85rem; + color: var(--text-main); + font-size: 0.9rem; + outline: none; + transition: border-color 0.2s; +} + +.form-control:focus, .form-select:focus { + border-color: var(--accent-cyan); + box-shadow: 0 0 0 2px rgba(6, 182, 212, 0.2); +} + +textarea.form-control { + font-family: 'Fira Code', monospace; + font-size: 0.85rem; + line-height: 1.4; +} + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.65rem 1.25rem; + font-size: 0.9rem; + font-weight: 600; + border-radius: 0.5rem; + border: none; + cursor: pointer; + transition: all 0.2s; +} + +.btn-primary { + background: linear-gradient(135deg, var(--accent-cyan), var(--accent-blue)); + color: #fff; +} + +.btn-primary:hover { + opacity: 0.9; + transform: translateY(-1px); +} + +.btn-secondary { + background: #334155; + color: #fff; +} + +.btn-secondary:hover { + background: #475569; +} + +.btn-danger { + background: var(--accent-red); + color: #fff; +} + +/* JSON Output Inspector */ +.json-viewer { + background: #090d16; + border: 1px solid var(--border-color); + border-radius: 0.5rem; + padding: 1rem; + font-family: 'Fira Code', 'Courier New', monospace; + font-size: 0.85rem; + color: #38bdf8; + max-height: 400px; + overflow-y: auto; + white-space: pre-wrap; + word-break: break-all; +} + +/* Table */ +.data-table { + width: 100%; + border-collapse: collapse; + font-size: 0.875rem; +} + +.data-table th { + background: rgba(15, 23, 42, 0.6); + color: var(--text-muted); + font-weight: 600; + text-align: left; + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--border-color); +} + +.data-table td { + padding: 0.75rem 1rem; + border-bottom: 1px solid rgba(51, 65, 85, 0.4); +} + +.data-table tr:hover { + background: rgba(255, 255, 255, 0.02); +} + +/* Badges */ +.badge { + display: inline-block; + padding: 0.2rem 0.55rem; + border-radius: 0.35rem; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; +} + +.badge-success { background: rgba(16, 185, 129, 0.2); color: #34d399; border: 1px solid rgba(16, 185, 129, 0.4); } +.badge-error { background: rgba(239, 68, 68, 0.2); color: #f87171; border: 1px solid rgba(239, 68, 68, 0.4); } +.badge-forward { background: rgba(59, 130, 246, 0.2); color: #60a5fa; border: 1px solid rgba(59, 130, 246, 0.4); } +.badge-reverse { background: rgba(236, 72, 153, 0.2); color: #f472b6; border: 1px solid rgba(236, 72, 153, 0.4); } +.badge-webhook { background: rgba(139, 92, 246, 0.2); color: #a78bfa; border: 1px solid rgba(139, 92, 246, 0.4); } + +/* Modal */ +.modal { + display: none; + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0, 0, 0, 0.8); + backdrop-filter: blur(4px); + z-index: 1000; + align-items: center; + justify-content: center; +} + +.modal.active { + display: flex; +} + +.modal-content { + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: 0.75rem; + width: 90%; + max-width: 800px; + max-height: 90vh; + overflow-y: auto; + padding: 1.5rem; +} diff --git a/shipping/templates/shipping/base.html b/shipping/templates/shipping/base.html new file mode 100644 index 0000000..fcd4bd2 --- /dev/null +++ b/shipping/templates/shipping/base.html @@ -0,0 +1,44 @@ +{% load static %} + + + + + + {% block title %}Shadowfax (SFX) Shipping Platform{% endblock %} + + + + + {% block extra_head %}{% endblock %} + + + + + + +
+ {% block content %}{% endblock %} +
+ + {% block extra_scripts %}{% endblock %} + + diff --git a/shipping/templates/shipping/forward.html b/shipping/templates/shipping/forward.html new file mode 100644 index 0000000..a4a924d --- /dev/null +++ b/shipping/templates/shipping/forward.html @@ -0,0 +1,330 @@ +{% extends 'shipping/base.html' %} + +{% block title %}Forward Pickup & Delivery — SFX API Hub{% endblock %} + +{% block content %} + + + +
+
+ 🌐 API Configuration & Environment Setup + Forward Logistics API +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+ +
+
1. Pincode Serviceability Check
+
+
+ + +
+
+ + +
+ +
+
+ + +
+
2. Pre-allocate AWB Numbers
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
3. Create Marketplace Order (Seller Pickup)
+
+
+ + +
+ +
+
+ + +
+
4. Create Warehouse Order (Hub Dispatch)
+
+
+ + +
+ +
+
+
+ + +
+ +
+
+ ⚡ Live API Response & Inspector + Awaiting Request +
+
+ Execution Time: - ms + HTTP Status: - +
+
// Select an action on the left to execute API call and inspect response payload...
+
+ + +
+
5. Track Single Order V4
+
+
+ + +
+ +
+
+ + +
+
6. Track Bulk Orders V4
+
+
+ + +
+ +
+
+ + +
+
7. Cancel Order
+
+
+
+ + +
+
+ + +
+
+
+ + +
+ +
+
+ + +
+
8. Generate Dynamic UPI QR Code V2
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
9. Get POD Details
+
+
+ + +
+ +
+
+
+
+ +{% endblock %} + +{% block extra_scripts %} + +{% endblock %} diff --git a/shipping/templates/shipping/logs.html b/shipping/templates/shipping/logs.html new file mode 100644 index 0000000..6a43a06 --- /dev/null +++ b/shipping/templates/shipping/logs.html @@ -0,0 +1,183 @@ +{% extends 'shipping/base.html' %} + +{% block title %}Activity Audit Logs — SFX API Hub{% endblock %} + +{% block content %} + + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ + +
+
+ Audit Log History (Showing Latest {{ logs|length }} Entries) +
+ + + + + + + + + + + + + + {% for log in logs %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
TimestampServiceAction NameMethod & EndpointStatus CodeLatencyActions
+ {{ log.timestamp|date:"Y-m-d H:i:s" }} + + {% if log.service_type == 'FORWARD' %} + Forward + {% elif log.service_type == 'REVERSE' %} + Reverse + {% else %} + Webhook + {% endif %} + {{ log.action_name }} + {{ log.http_method }} + {{ log.endpoint_url }} + + {% if log.response_status_code >= 200 and log.response_status_code < 300 %} + {{ log.response_status_code }} OK + {% else %} + {{ log.response_status_code }} Error + {% endif %} + + {{ log.execution_time_ms }} ms + + +
+ No audit logs recorded matching criteria. Execute API calls from Forward or Reverse hubs to populate logs. +
+
+ + + +{% endblock %} + +{% block extra_scripts %} + +{% endblock %} diff --git a/shipping/templates/shipping/reverse.html b/shipping/templates/shipping/reverse.html new file mode 100644 index 0000000..9b88dd8 --- /dev/null +++ b/shipping/templates/shipping/reverse.html @@ -0,0 +1,266 @@ +{% extends 'shipping/base.html' %} + +{% block title %}Reverse Pickup (QC) — SFX API Hub{% endblock %} + +{% block content %} + + + +
+
+ 🌐 API Configuration & Environment Setup + Reverse Logistics API +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+ +
+
1. Reverse Pincode Serviceability Check
+
+
+ + +
+ +
+
+ + +
+
2. Pre-allocate Reverse AWB Numbers
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
3. Create Reverse Pickup Request (Doorstep QC)
+
+
+ + +
+ +
+
+ + +
+
4. Update QC Flags & Serial Number
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ +
+
+
+ + +
+ +
+
+ ⚡ Live Reverse Response Inspector + Awaiting Request +
+
+ Execution Time: - ms + HTTP Status: - +
+
// Select a reverse action on the left to test API call and inspect response payload...
+
+ + +
+
5. Track Single Reverse Pickup V4
+
+
+ + +
+ +
+
+ + +
+
6. Bulk Track Reverse Pickups V4
+
+
+ + +
+ +
+
+ + +
+
7. Cancel Reverse Pickup Request
+
+
+ + +
+
+ + +
+ +
+
+ + +
+
8. Get Proof of Pickup (POP) & QC Photos
+
+
+ + +
+ +
+
+
+
+{% endblock %} + +{% block extra_scripts %} + +{% endblock %} diff --git a/shipping/templates/shipping/webhooks.html b/shipping/templates/shipping/webhooks.html new file mode 100644 index 0000000..f987fa0 --- /dev/null +++ b/shipping/templates/shipping/webhooks.html @@ -0,0 +1,176 @@ +{% extends 'shipping/base.html' %} + +{% block title %}Webhook Listener & Simulator — SFX API Hub{% endblock %} + +{% block content %} + + + +
+
+
+ 📡 Live Webhook Listener Endpoint + Active +
+
+ + +
+

Configure this endpoint in your Shadowfax Merchant Portal to receive HTTP POST JSON notifications for shipment status updates and doorstep Quality Check results.

+
+ +
+
+ 🧪 Trigger Mock Webhook Callback + Simulator Tool +
+
+ {% csrf_token %} +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ Received Webhook Events History ({{ webhook_logs|length }} Events) +
+ + + + + + + + + + + + + {% for log in webhook_logs %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
TimestampEvent NameEndpointHTTP StatusReceived Payload PreviewAction
+ {{ log.timestamp|date:"Y-m-d H:i:s" }} + + {{ log.action_name }} + + {{ log.endpoint_url }} + + {{ log.response_status_code }} OK + + {{ log.request_body }} + + +
+ No webhook callback events received yet. Use the Simulator Tool above or send a POST request to http://localhost:8000/api/webhook/. +
+
+ + + +{% endblock %} + +{% block extra_scripts %} + +{% endblock %} diff --git a/shipping/tests.py b/shipping/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/shipping/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/shipping/urls.py b/shipping/urls.py new file mode 100644 index 0000000..984e8fd --- /dev/null +++ b/shipping/urls.py @@ -0,0 +1,20 @@ +from django.urls import path +from . import views + +urlpatterns = [ + # Dashboard Web UI Routes + path('', views.index_redirect, name='index_redirect'), + path('forward/', views.forward_view, name='forward_view'), + path('reverse/', views.reverse_view, name='reverse_view'), + path('logs/', views.logs_view, name='logs_view'), + path('logs//', views.log_detail_api, name='log_detail_api'), + path('logs/clear/', views.clear_logs_view, name='clear_logs_view'), + path('webhooks/', views.webhooks_ui_view, name='webhooks_ui_view'), + path('api/webhook/', views.webhook_listener_api, name='webhook_listener_api'), + + # Microservices REST API Endpoints under /api/v1/ + path('api/v1/health/', views.api_healthcheck, name='api_healthcheck'), + path('api/v1/forward//', views.api_forward_router, name='api_forward_router'), + path('api/v1/reverse//', views.api_reverse_router, name='api_reverse_router'), + path('api/v1/logs/', views.api_logs_export, name='api_logs_export'), +] diff --git a/shipping/views.py b/shipping/views.py new file mode 100644 index 0000000..38d506c --- /dev/null +++ b/shipping/views.py @@ -0,0 +1,650 @@ +import json +import time +from django.shortcuts import render, redirect, get_object_or_404 +from django.http import JsonResponse, HttpResponse +from django.views.decorators.csrf import csrf_exempt +from django.db.models import Q + +from .models import ActivityLog +from .sfx_client import execute_sfx_call, get_base_url + +def index_redirect(request): + return redirect('forward_view') + +def forward_view(request): + if request.method == 'POST': + action = request.POST.get('action_type') + env = request.POST.get('environment', 'staging') + token = request.POST.get('api_token', 'sample_staging_token_12345') + base_url = get_base_url(env) + + headers = { + 'Authorization': f'Token {token.strip()}', + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + + # Handle Mock/Simulation Mode toggle if real upstream fails or is blocked + is_mock = request.POST.get('use_mock', 'false') == 'true' + + if action == 'serviceability': + pincodes = request.POST.get('pincodes', '560077,110009') + service = request.POST.get('service', 'express') + url = f"{base_url}/v1/clients/serviceability/?service={service}&pincodes={pincodes}" + + if is_mock: + res_data = { + "status": "success", + "data": { + "pincodes": [ + {"pincode": "560077", "city": "Bengaluru", "serviceable": True, "cod_available": True}, + {"pincode": "110009", "city": "New Delhi", "serviceable": True, "cod_available": True} + ] + } + } + log_res = record_mock_call('FORWARD', 'Serviceability Check', 'GET', url, headers, None, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Serviceability Check', 'GET', url, headers, env=env) + return JsonResponse(res) + + elif action == 'generate_awb': + count = int(request.POST.get('count', 1)) + client_code = request.POST.get('client_code', 'DEMO_CLIENT') + url = f"{base_url}/v3/clients/generate_marketplace_awb/" + payload = {"client_code": client_code, "count": count} + + if is_mock: + res_data = {"status": "success", "data": {"awb_numbers": [f"SFX{int(time.time())}99"]}} + log_res = record_mock_call('FORWARD', 'Generate AWB', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Generate AWB', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'create_marketplace_order': + raw_payload = request.POST.get('payload', '{}') + try: + payload = json.loads(raw_payload) + except Exception as e: + return JsonResponse({'status': 'ERROR', 'response': f'Invalid JSON payload: {e}'}, status=400) + + url = f"{base_url}/v3/clients/orders/" + + if is_mock: + awb = payload.get('order_details', {}).get('awb_number') or f"SFX{int(time.time())}01" + order_id = payload.get('order_details', {}).get('client_order_id') or "ORD-MKT-101" + res_data = {"status": "success", "message": "Order created successfully", "data": {"client_order_id": order_id, "awb_number": awb, "order_status": "new"}} + log_res = record_mock_call('FORWARD', 'Create Marketplace Order', 'POST', url, headers, payload, 201, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Create Marketplace Order', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'create_warehouse_order': + raw_payload = request.POST.get('payload', '{}') + try: + payload = json.loads(raw_payload) + except Exception as e: + return JsonResponse({'status': 'ERROR', 'response': f'Invalid JSON payload: {e}'}, status=400) + + url = f"{base_url}/v3/clients/orders/" + + if is_mock: + awb = payload.get('order_details', {}).get('awb_number') or f"SFX{int(time.time())}02" + order_id = payload.get('order_details', {}).get('client_order_id') or "ORD-WH-505" + res_data = {"status": "success", "message": "Warehouse order created", "data": {"client_order_id": order_id, "awb_number": awb, "order_status": "new"}} + log_res = record_mock_call('FORWARD', 'Create Warehouse Order', 'POST', url, headers, payload, 201, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Create Warehouse Order', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'track_single': + awb = request.POST.get('awb_number', 'SFX9928374102') + url = f"{base_url}/v4/clients/orders/{awb}/track/" + + if is_mock: + res_data = { + "status": "success", + "data": { + "awb_number": awb, + "current_status": "out_for_delivery", + "rider_details": {"name": "Rajesh Kumar", "phone": "+919876543210"}, + "tracking_history": [ + {"status": "new", "timestamp": "2026-09-25T08:00:00Z", "location": "Origin Hub"}, + {"status": "out_for_delivery", "timestamp": "2026-09-25T14:00:00Z", "location": "Destination Hub"} + ] + } + } + log_res = record_mock_call('FORWARD', 'Track Single Order V4', 'GET', url, headers, None, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Track Single Order V4', 'GET', url, headers, env=env) + return JsonResponse(res) + + elif action == 'track_bulk': + awbs = request.POST.get('awb_numbers', 'SFX9928374102,SFX8817263541').split(',') + url = f"{base_url}/v4/clients/bulk_track/" + payload = {"awb_numbers": [a.strip() for a in awbs if a.strip()]} + + if is_mock: + res_data = { + "status": "success", + "data": [ + {"awb_number": a, "current_status": "in_transit"} for a in payload["awb_numbers"] + ] + } + log_res = record_mock_call('FORWARD', 'Track Bulk Orders V4', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Track Bulk Orders V4', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'cancel_order': + client_order_id = request.POST.get('client_order_id', 'ORD-MKT-101') + awb = request.POST.get('awb_number', 'SFX9928374102') + reason = request.POST.get('reason', 'Customer requested cancellation') + url = f"{base_url}/v3/clients/orders/cancel/" + payload = {"client_order_id": client_order_id, "awb_number": awb, "reason": reason} + + if is_mock: + res_data = {"status": "success", "message": "Order cancelled successfully", "data": {"awb_number": awb, "status": "cancelled"}} + log_res = record_mock_call('FORWARD', 'Cancel Order', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Cancel Order', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'escalation': + awb = request.POST.get('awb_number', 'SFX9928374102') + category = request.POST.get('category', 'EXPEDITE_PICKUP') + description = request.POST.get('description', 'Pickup store closing early') + url = f"{base_url}/v1/clients/support/issue/" + payload = {"awb_number": awb, "category": category, "description": description} + + if is_mock: + res_data = {"status": "success", "message": "Ticket raised", "data": {"ticket_id": "TKT-99120", "category": category}} + log_res = record_mock_call('FORWARD', 'Raise Escalation Ticket', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Raise Escalation Ticket', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'get_pod': + awb = request.POST.get('awb_number', 'SFX9928374102') + url = f"{base_url}/v1/clients/pod_details/" + payload = {"awb_number": awb} + + if is_mock: + res_data = {"status": "success", "data": {"awb_number": awb, "signature_url": f"https://cdn.shadowfax.in/pod/{awb}.png", "otp_verified": True}} + log_res = record_mock_call('FORWARD', 'Get POD Details', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Get POD Details', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'generate_qr': + awb = request.POST.get('awb_number', 'SFX9928374102') + cod_amount = float(request.POST.get('cod_amount', 1499.0)) + url = f"{base_url}/v2/clients/generate_qr_code/" + payload = {"awb_number": awb, "cod_amount": cod_amount} + + if is_mock: + res_data = {"status": "success", "data": {"awb_number": awb, "cod_amount": cod_amount, "qr_code_string": f"upi://pay?pa=shadowfax@icici&am={cod_amount}"}} + log_res = record_mock_call('FORWARD', 'Generate Dynamic QR Code V2', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('FORWARD', 'Generate Dynamic QR Code V2', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + # GET Request: render template + recent_logs = ActivityLog.objects.filter(service_type='FORWARD')[:10] + return render(request, 'shipping/forward.html', {'recent_logs': recent_logs}) + + +def reverse_view(request): + if request.method == 'POST': + action = request.POST.get('action_type') + env = request.POST.get('environment', 'staging') + token = request.POST.get('api_token', 'sample_staging_token_12345') + base_url = get_base_url(env) + + headers = { + 'Authorization': f'Token {token.strip()}', + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + + is_mock = request.POST.get('use_mock', 'false') == 'true' + + if action == 'reverse_serviceability': + pincodes = request.POST.get('pincodes', '560077,110009') + url = f"{base_url}/v1/clients/serviceability/?service=reverse&pincodes={pincodes}" + + if is_mock: + res_data = {"status": "success", "data": {"pincodes": [{"pincode": "560077", "reverse_pickup_available": True}]}} + log_res = record_mock_call('REVERSE', 'Reverse Serviceability Check', 'GET', url, headers, None, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('REVERSE', 'Reverse Serviceability Check', 'GET', url, headers, env=env) + return JsonResponse(res) + + elif action == 'generate_reverse_awb': + count = int(request.POST.get('count', 1)) + client_code = request.POST.get('client_code', 'DEMO_CLIENT') + url = f"{base_url}/v3/clients/orders/generate_awb/" + payload = {"client_code": client_code, "count": count} + + if is_mock: + res_data = {"status": "success", "data": {"awb_numbers": [f"SFX-REV-{int(time.time())}99"]}} + log_res = record_mock_call('REVERSE', 'Generate Reverse AWB', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('REVERSE', 'Generate Reverse AWB', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'create_reverse_request': + raw_payload = request.POST.get('payload', '{}') + try: + payload = json.loads(raw_payload) + except Exception as e: + return JsonResponse({'status': 'ERROR', 'response': f'Invalid JSON payload: {e}'}, status=400) + + url = f"{base_url}/v3/clients/requests" + + if is_mock: + req_id = payload.get('client_request_id') or "REV-ORD-8801" + awb = payload.get('awb_number') or f"SFX-REV-{int(time.time())}01" + res_data = {"status": "success", "message": "Reverse pickup request created", "data": {"client_request_id": req_id, "awb_number": awb, "status": "new"}} + log_res = record_mock_call('REVERSE', 'Create Reverse Pickup Request', 'POST', url, headers, payload, 201, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('REVERSE', 'Create Reverse Pickup Request', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'track_reverse_single': + req_id = request.POST.get('client_request_id', 'REV-ORD-8801') + url = f"{base_url}/v4/clients/requests/{req_id}" + + if is_mock: + res_data = { + "status": "success", + "data": { + "client_request_id": req_id, + "current_status": "picked_up", + "qc_status": "PASSED", + "rider_details": {"name": "Ramesh Kumar", "phone": "+919876543210"}, + "tracking_history": [ + {"status": "new", "timestamp": "2026-09-25T10:00:00Z"}, + {"status": "picked_up", "timestamp": "2026-09-25T12:00:00Z", "remarks": "Item passed QC"} + ] + } + } + log_res = record_mock_call('REVERSE', 'Track Reverse Pickup V4', 'GET', url, headers, None, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('REVERSE', 'Track Reverse Pickup V4', 'GET', url, headers, env=env) + return JsonResponse(res) + + elif action == 'track_reverse_bulk': + req_ids = request.POST.get('client_request_ids', 'REV-ORD-8801,REV-ORD-8802').split(',') + url = f"{base_url}/v4/clients/requests/bulk_query" + payload = {"client_request_ids": [r.strip() for r in req_ids if r.strip()]} + + if is_mock: + res_data = {"status": "success", "data": [{"client_request_id": r, "current_status": "picked_up"} for r in payload["client_request_ids"]]} + log_res = record_mock_call('REVERSE', 'Bulk Track Reverse Pickups V4', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('REVERSE', 'Bulk Track Reverse Pickups V4', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'update_qc': + req_id = request.POST.get('client_request_id', 'REV-ORD-8801') + qc_check = request.POST.get('qc_check', 'true') == 'true' + serial_number = request.POST.get('serial_number', 'SN-9988112233') + url = f"{base_url}/v2/clients/requests/update_qc/" + payload = {"client_request_id": req_id, "qc_check": qc_check, "serial_number": serial_number} + + if is_mock: + res_data = {"status": "success", "message": "QC Flag updated", "data": payload} + log_res = record_mock_call('REVERSE', 'Update QC Flag', 'PUT', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('REVERSE', 'Update QC Flag', 'PUT', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'cancel_reverse': + req_id = request.POST.get('client_request_id', 'REV-ORD-8801') + reason = request.POST.get('reason', 'Customer withdrew return request') + url = f"{base_url}/v2/clients/requests/mark_cancel" + payload = {"client_request_id": req_id, "reason": reason} + + if is_mock: + res_data = {"status": "success", "message": "Reverse request cancelled", "data": {"client_request_id": req_id, "status": "cancelled"}} + log_res = record_mock_call('REVERSE', 'Cancel Reverse Request', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('REVERSE', 'Cancel Reverse Request', 'POST', url, headers, payload, env=env) + return JsonResponse(res) + + elif action == 'get_reverse_pod': + req_id = request.POST.get('client_request_id', 'REV-ORD-8801') + url = f"{base_url}/v1/clients/pod_details/?client_request_id={req_id}" + + if is_mock: + res_data = {"status": "success", "data": {"client_request_id": req_id, "signature_url": f"https://cdn.shadowfax.in/pop/{req_id}.png", "otp_verified": True}} + log_res = record_mock_call('REVERSE', 'Get Proof of Pickup (POP)', 'GET', url, headers, None, 200, res_data) + return JsonResponse(log_res) + + res = execute_sfx_call('REVERSE', 'Get Proof of Pickup (POP)', 'GET', url, headers, env=env) + return JsonResponse(res) + + recent_logs = ActivityLog.objects.filter(service_type='REVERSE')[:10] + return render(request, 'shipping/reverse.html', {'recent_logs': recent_logs}) + + +def logs_view(request): + query = request.GET.get('q', '') + service_filter = request.GET.get('service', '') + status_filter = request.GET.get('status', '') + + logs = ActivityLog.objects.all() + + if service_filter: + logs = logs.filter(service_type=service_filter) + if status_filter: + logs = logs.filter(status=status_filter) + if query: + logs = logs.filter( + Q(action_name__icontains=query) | + Q(endpoint_url__icontains=query) | + Q(request_body__icontains=query) | + Q(response_body__icontains=query) + ) + + return render(request, 'shipping/logs.html', { + 'logs': logs[:100], + 'query': query, + 'service_filter': service_filter, + 'status_filter': status_filter + }) + +def log_detail_api(request, log_id): + log_item = get_object_or_404(ActivityLog, id=log_id) + return JsonResponse({ + 'id': log_item.id, + 'service_type': log_item.service_type, + 'action_name': log_item.action_name, + 'endpoint_url': log_item.endpoint_url, + 'http_method': log_item.http_method, + 'request_headers': log_item.request_headers, + 'request_body': log_item.request_body, + 'response_status_code': log_item.response_status_code, + 'response_body': log_item.response_body, + 'execution_time_ms': log_item.execution_time_ms, + 'status': log_item.status, + 'timestamp': log_item.timestamp.isoformat() + }) + +def clear_logs_view(request): + if request.method == 'POST': + ActivityLog.objects.all().delete() + return redirect('logs_view') + + +@csrf_exempt +def webhook_listener_api(request): + if request.method == 'POST': + try: + body_unicode = request.body.decode('utf-8') + try: + body_data = json.loads(body_unicode) + body_formatted = json.dumps(body_data, indent=2) + except Exception: + body_formatted = body_unicode + except Exception as e: + body_formatted = f"Raw body error: {e}" + + headers_dict = {k: v for k, v in request.META.items() if k.startswith('HTTP_') or k in ['CONTENT_TYPE', 'CONTENT_LENGTH']} + headers_formatted = json.dumps(headers_dict, indent=2) + + log_entry = ActivityLog.objects.create( + service_type='WEBHOOK', + action_name='Incoming Webhook Callback', + endpoint_url=request.build_absolute_uri(), + http_method='POST', + request_headers=headers_formatted, + request_body=body_formatted, + response_status_code=200, + response_body=json.dumps({"status": "received", "timestamp": time.time()}), + execution_time_ms=1.5, + status='SUCCESS' + ) + return JsonResponse({"status": "success", "message": "Webhook logged", "log_id": log_entry.id}) + + return JsonResponse({"error": "POST method required"}, status=405) + + +def webhooks_ui_view(request): + if request.method == 'POST': + # Simulate webhook trigger + event_type = request.POST.get('event_type', 'out_for_delivery') + order_id = request.POST.get('order_id', 'ORD-MKT-2026-101') + awb = request.POST.get('awb_number', 'SFX9928374102') + service_type = request.POST.get('service_type', 'FORWARD') + + sim_payload = { + "client_order_id": order_id, + "awb_number": awb, + "status": event_type, + "sub_status": event_type, + "remarks": f"Simulated {service_type} webhook event: {event_type}", + "reason_code": "SIM_OK", + "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ") + } + + ActivityLog.objects.create( + service_type='WEBHOOK', + action_name=f'Simulated Callback ({service_type}: {event_type})', + endpoint_url='/api/webhook/simulator', + http_method='POST', + request_headers=json.dumps({"Content-Type": "application/json", "X-Simulated": "True"}, indent=2), + request_body=json.dumps(sim_payload, indent=2), + response_status_code=200, + response_body=json.dumps({"status": "simulated_success"}), + execution_time_ms=0.8, + status='SUCCESS' + ) + return redirect('webhooks_ui_view') + + webhook_logs = ActivityLog.objects.filter(service_type='WEBHOOK')[:30] + return render(request, 'shipping/webhooks.html', {'webhook_logs': webhook_logs}) + + +def record_mock_call(service_type, action_name, method, url, headers, payload, status_code, response_data): + req_body_str = json.dumps(payload, indent=2) if payload else "" + req_headers_str = json.dumps(headers, indent=2) if headers else "" + res_body_str = json.dumps(response_data, indent=2) + + log_entry = ActivityLog.objects.create( + service_type=service_type, + action_name=f"{action_name} (Simulation)", + endpoint_url=url, + http_method=method, + request_headers=req_headers_str, + request_body=req_body_str, + response_status_code=status_code, + response_body=res_body_str, + execution_time_ms=3.2, + status='SUCCESS' + ) + return { + "log_id": log_entry.id, + "status_code": status_code, + "execution_time_ms": 3.2, + "status": "SUCCESS", + "response": res_body_str + } + + +# ============================================================================== +# REST API Endpoints for External Microservices (e.g. seller_central_backend) +# ============================================================================== + +@csrf_exempt +def api_healthcheck(request): + return JsonResponse({ + "status": "healthy", + "service": "Shadowfax Shipping Microservice", + "timestamp": time.time() + }) + +@csrf_exempt +def api_forward_router(request, action_name): + if request.method not in ['POST', 'GET']: + return JsonResponse({'error': 'POST or GET method required'}, status=405) + + try: + data = json.loads(request.body.decode('utf-8')) if request.body else {} + except Exception: + data = {} + + token = data.get('token') or request.headers.get('Authorization', '').replace('Token ', '') or 'sample_staging_token_12345' + env = data.get('env') or 'staging' + is_mock = data.get('use_mock', True) + base_url = get_base_url(env) + headers = { + 'Authorization': f'Token {token.strip()}', + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + + if action_name == 'serviceability': + pincodes = data.get('pincodes', '560077,110009') + service = data.get('service', 'express') + url = f"{base_url}/v1/clients/serviceability/?service={service}&pincodes={pincodes}" + if is_mock: + res_data = {"status": "success", "data": {"pincodes": [{"pincode": "560077", "city": "Bengaluru", "serviceable": True}]}} + log_res = record_mock_call('FORWARD', 'API: Serviceability Check', 'GET', url, headers, None, 200, res_data) + return JsonResponse(log_res) + return JsonResponse(execute_sfx_call('FORWARD', 'API: Serviceability Check', 'GET', url, headers, env=env)) + + elif action_name in ['create-order', 'create_order']: + payload = data.get('payload', data) + url = f"{base_url}/v3/clients/orders/" + if is_mock: + awb = payload.get('order_details', {}).get('awb_number') or f"SFX{int(time.time())}01" + order_id = payload.get('order_details', {}).get('client_order_id') or "ORD-MKT-101" + res_data = {"status": "success", "message": "Order created", "data": {"client_order_id": order_id, "awb_number": awb, "order_status": "new"}} + log_res = record_mock_call('FORWARD', 'API: Create Order', 'POST', url, headers, payload, 201, res_data) + return JsonResponse(log_res) + return JsonResponse(execute_sfx_call('FORWARD', 'API: Create Order', 'POST', url, headers, payload, env=env)) + + elif action_name == 'track': + awb = request.GET.get('awb') or data.get('awb_number', 'SFX9928374102') + url = f"{base_url}/v4/clients/orders/{awb}/track/" + if is_mock: + res_data = {"status": "success", "data": {"awb_number": awb, "current_status": "out_for_delivery"}} + log_res = record_mock_call('FORWARD', 'API: Track Order', 'GET', url, headers, None, 200, res_data) + return JsonResponse(log_res) + return JsonResponse(execute_sfx_call('FORWARD', 'API: Track Order', 'GET', url, headers, env=env)) + + elif action_name == 'cancel': + payload = data.get('payload', data) + url = f"{base_url}/v3/clients/orders/cancel/" + if is_mock: + res_data = {"status": "success", "message": "Order cancelled"} + log_res = record_mock_call('FORWARD', 'API: Cancel Order', 'POST', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + return JsonResponse(execute_sfx_call('FORWARD', 'API: Cancel Order', 'POST', url, headers, payload, env=env)) + + return JsonResponse({'error': f'Action {action_name} not supported'}, status=400) + + +@csrf_exempt +def api_reverse_router(request, action_name): + if request.method not in ['POST', 'GET', 'PUT']: + return JsonResponse({'error': 'POST, GET or PUT method required'}, status=405) + + try: + data = json.loads(request.body.decode('utf-8')) if request.body else {} + except Exception: + data = {} + + token = data.get('token') or request.headers.get('Authorization', '').replace('Token ', '') or 'sample_staging_token_12345' + env = data.get('env') or 'staging' + is_mock = data.get('use_mock', True) + base_url = get_base_url(env) + headers = { + 'Authorization': f'Token {token.strip()}', + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + + if action_name == 'serviceability': + pincodes = data.get('pincodes', '560077,110009') + url = f"{base_url}/v1/clients/serviceability/?service=reverse&pincodes={pincodes}" + if is_mock: + res_data = {"status": "success", "data": {"pincodes": [{"pincode": "560077", "reverse_pickup_available": True}]}} + log_res = record_mock_call('REVERSE', 'API: Reverse Serviceability Check', 'GET', url, headers, None, 200, res_data) + return JsonResponse(log_res) + return JsonResponse(execute_sfx_call('REVERSE', 'API: Reverse Serviceability Check', 'GET', url, headers, env=env)) + + elif action_name in ['create-request', 'create_request']: + payload = data.get('payload', data) + url = f"{base_url}/v3/clients/requests" + if is_mock: + req_id = payload.get('client_request_id') or "REV-ORD-8801" + awb = payload.get('awb_number') or f"SFX-REV-{int(time.time())}01" + res_data = {"status": "success", "message": "Reverse pickup request created", "data": {"client_request_id": req_id, "awb_number": awb, "status": "new"}} + log_res = record_mock_call('REVERSE', 'API: Create Reverse Request', 'POST', url, headers, payload, 201, res_data) + return JsonResponse(log_res) + return JsonResponse(execute_sfx_call('REVERSE', 'API: Create Reverse Request', 'POST', url, headers, payload, env=env)) + + elif action_name == 'track': + req_id = request.GET.get('client_request_id') or data.get('client_request_id', 'REV-ORD-8801') + url = f"{base_url}/v4/clients/requests/{req_id}" + if is_mock: + res_data = {"status": "success", "data": {"client_request_id": req_id, "current_status": "picked_up", "qc_status": "PASSED"}} + log_res = record_mock_call('REVERSE', 'API: Track Reverse Request', 'GET', url, headers, None, 200, res_data) + return JsonResponse(log_res) + return JsonResponse(execute_sfx_call('REVERSE', 'API: Track Reverse Request', 'GET', url, headers, env=env)) + + elif action_name in ['update-qc', 'update_qc']: + payload = data.get('payload', data) + url = f"{base_url}/v2/clients/requests/update_qc/" + if is_mock: + res_data = {"status": "success", "message": "QC Flag updated", "data": payload} + log_res = record_mock_call('REVERSE', 'API: Update QC Flag', 'PUT', url, headers, payload, 200, res_data) + return JsonResponse(log_res) + return JsonResponse(execute_sfx_call('REVERSE', 'API: Update QC Flag', 'PUT', url, headers, payload, env=env)) + + return JsonResponse({'error': f'Action {action_name} not supported'}, status=400) + + +@csrf_exempt +def api_logs_export(request): + query = request.GET.get('q', '') + service_filter = request.GET.get('service', '') + logs = ActivityLog.objects.all() + if service_filter: + logs = logs.filter(service_type=service_filter) + if query: + logs = logs.filter(Q(action_name__icontains=query) | Q(request_body__icontains=query) | Q(response_body__icontains=query)) + + logs_data = [ + { + "id": l.id, + "service_type": l.service_type, + "action_name": l.action_name, + "endpoint_url": l.endpoint_url, + "http_method": l.http_method, + "response_status_code": l.response_status_code, + "execution_time_ms": l.execution_time_ms, + "status": l.status, + "timestamp": l.timestamp.isoformat() + } for l in logs[:100] + ] + return JsonResponse({"count": len(logs_data), "logs": logs_data}) diff --git a/shippingservice-beta.service b/shippingservice-beta.service new file mode 100644 index 0000000..00a8b52 --- /dev/null +++ b/shippingservice-beta.service @@ -0,0 +1,21 @@ +[Unit] +Description=Shipping Service Beta Service +After=network.target + +[Service] +User=ubuntu +WorkingDirectory=/home/ubuntu/shippingservice +EnvironmentFile=/home/ubuntu/shippingservice/.env.beta +ExecStart=/home/ubuntu/shippingservice/venv/bin/gunicorn shippingservice_project.wsgi:application \ + --bind 0.0.0.0:8086 \ + --workers 3 \ + --access-logfile /home/ubuntu/shippingservice/logs/gunicorn-beta-access.log \ + --error-logfile /home/ubuntu/shippingservice/logs/gunicorn-beta-error.log \ + --log-level info \ + --capture-output + +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/shippingservice_project/__init__.py b/shippingservice_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/shippingservice_project/__pycache__/__init__.cpython-312.pyc b/shippingservice_project/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d97fa3588b16deb238987551546a9bdd88c3d2ea GIT binary patch literal 169 zcmX@j%ge<81h1!V%L37lK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%R@gSKQ~psEHgbX zwKzjRJ+maEG)ccWBeS3&GcUb3wWusJITf8BUr>~vm6}|lA0MBYmst`YuUAm{i^C>2 cKczG$)vkyYXfPuX7lRldnHd=wiOV literal 0 HcmV?d00001 diff --git a/shippingservice_project/__pycache__/settings.cpython-312.pyc b/shippingservice_project/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..100ab5fc928f9f543bc3b74fcc992a23465d0f4c GIT binary patch literal 3304 zcmb7Gy;s{<7ME-ze;Wf%ViJ;37Ls5x$OB2p%~G^)6dFb0*HDY~*_rOHgU^1x!Z;pB;U}{cr904=N~3XM z0!@6-YXnd9y?#*V61x0RohvB%QJqP2^`ko1&{SUsU4O}-2{eW-qbRzHrh4@FZ|EB@ zok9eChFC!JGj!8_{?2|*%XHNxTtYL#Wpqop0w)uN?ZWZP;5^6 z1{%Sa+L6?%#v0f(WwmOsTbj-q+j6~bQCQ!VD_E@S+77OmY;Wa!z7?!uT{1CZ%lqsd zWc!$xP)$}@!<2Nhr+J5M7~qHP1zF-2;LqoK3^8h&j#*iSa5c%4HI;>bb6bW4WCec? z`C#9+Y1WOe<8h=_46YlapFu^d#jzR(hjCM@%M~NuFCgA^Z*F6DtDz{YA{!>FZJie= zsR#mLS!Z`8rGbtACzg)41_J%p>ckjLTx*iK-CXXWAgczhG;};~?3lBPq~m+f>bvEq zTe~ym{f7(clKMb>HviTASZqm~#d}}QnP2VC%-mj-jJi}J^jyr{jC4J56-_mDxy;$P zHYbL;4G4N|h-sN%)in3(*l1oE5*pYr0O|JyH4sQuu(&#GVZzx|tP?Sar{5Kj;e1c8 zCL^TaJt&g1i@s{R4!>RA5k^*EiMU< z4H@qZs6HIZMFJQH^GE;<2+9?^D+dgRi|B(kXq@c=IbgMPK@o~vd9ou_p1@>q-#^x+ z8qU|P5ikbqrut|P%^=s%6{8vNc{OpZuD}>_WpIa8)E^TrWlv?Qn2WfV6Up!RT#C3- z=|Yis5^HOsy>sW+O6h#A*qr?kEPF;(=A?QZu(b~^M|`Po6SLkn<=E!^W4zJT??qAgg<7fQtS;QqbE7(*C7x025B z#AOF`+0i^_5nPrNxPrhU<|J-}x1@$*>H)Z#h|4NPkHWUOI&>m-+gsV}X*v?&CX|tl z8@CP(moTk+r(xovT$AAngK6IAYG``%(WiRK%Boyz)G}DDn%n30fBaPY?v$_fay)nN zr%!c|Yt*nVSKjw9g4OwkL7eNQWYZ1rE134UejQ-a-4<58cMWtm+-o&SR?b@h`q$7D z%<&d`xjS^RDdHAIcu9#Oao6EhpvYz2Y2oh?MeFXg_|P_*vWp2*hs#nmiDx-c;0 z)tgF`5>`${5>2d^R?k{k+(=~7Ak4!+1~V{G>xmU!Oy*NOL`!FRQOHAYdY~)C^um`p z!dSbK0A-aRY|=`NxU-3LhA$NVPQ8Oz)Gw>?ZLNmmz1fIYWplexjt@=eFx~$Za`pn| z>ifTVzNu-bq2NdQAHcEo!`Ap0JUdRBrcWI{+V#IEMNghGR3Lod`Rk)M{+mbso5%j? zH~zUJ|J)n@!jXTW<>`c|P=RjwIt=9vwnk3IA_pU_;9H+(+|dfXjd(&1kVU94*9vqz zRD`G7;qi7PdJHf@$<%U`18}CJIr*(ZQr*+*p6`R zFxw7a20`${g45|qyP9R1@{S&cg^tq!C3Mat4lT`EKuW>%yuRU2E0qkPFuD%y>$Y-OQ80io{v z3C2!XLJUl7Mcq1Kx6J@I+}-<}?>+g=whw`d?$c}Ztm>B*RapCGeW#i^Ab`LaB+!FU z_eN|a$U_PCFa!pmh#(51ty^WVtI^S4t<}QVvF#pK8545>cdVKPhG&gyj`gjv1Eo12 z2}>PA;(Snq(qgF}mxNAgTkc9Qu|>%8oTWj#hhRGgEYw6ewG%T4LMR)vayHy z5yy&fQ)4VxK_%8yHdIzu3d&RQ0@i8_HrH;LCDa|UAf+O716G9P(5+4i%16wn|8+m- zSw#K9)wb(i5m+gX2Ux0d)~Wum5W-{7{|`~}l=a}xjn literal 0 HcmV?d00001 diff --git a/shippingservice_project/__pycache__/wsgi.cpython-312.pyc b/shippingservice_project/__pycache__/wsgi.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe4355760686161e019c39cb88522a4236d39274 GIT binary patch literal 689 zcmZ`%&ubGw6rS0QP1)EWVtXFMOF(zpiw6-wNE;%Wf~g3(Y&JVDyAx(-mYqpzdh4lw zf%qqg^zJ|5rJ^A0Nf8n8qU6?-v-v?w!8y$P-aOv-zW3gI*x2xpJ-=VSAHBs0eJ`EW zsVu>4V8I*2P!BQ89D#e-T30x!+jB7jJX&9+T-Z_V0