API v1

API Reference

Complete reference for all ExportReady-Battery API endpoints. All endpoints use JSON for request and response bodies.

Base URL

https://api.exportready.com/api/v1

All endpoints are relative to this base URL. For local development, use http://localhost:8080/api/v1

Authentication

Protected endpoints require a Bearer token in the Authorization header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Access tokens expire after 15 minutes. Use the refresh token to obtain a new access token.

Authentication

User registration, login, and password management endpoints.

POST/auth/register

Register a new tenant/company account.

Request Body

{
 "company_name": "Acme Batteries",
 "email": "admin@acme.com",
 "password": "SecurePass123!"
}

Response

{
 "message": "Registration successful",
 "tenant_id": "550e8400-e29b-41d4-a716-446655440000"
}
POST/auth/login

Authenticate and receive access & refresh tokens.

Request Body

{
 "email": "admin@acme.com",
 "password": "SecurePass123!"
}

Response

{
 "token": "eyJhbGciOiJIUzI1NiI...",
 "refresh_token": "eyJhbGciOiJIUzI1NiI...",
 "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
 "email": "admin@acme.com",
 "company_name": "Acme Batteries",
 "expires_in": 900
}
POST/auth/refresh

Exchange a refresh token for a new access token.

Request Body

{
 "refresh_token": "eyJhbGciOiJIUzI1NiI..."
}

Response

{
 "token": "eyJhbGciOiJIUzI1NiI...",
 "expires_in": 900
}
POST/auth/forgot-password

Initiate password reset by sending a reset email.

Request Body

{
 "email": "admin@acme.com"
}

Response

{
 "message": "Password reset email sent"
}
POST/auth/reset-password

Complete password reset with token from email.

Request Body

{
 "token": "reset_token_from_email",
 "new_password": "NewSecurePass456!"
}

Response

{
 "message": "Password reset successful"
}
GET/auth/me๐Ÿ” Auth Required

Get the current authenticated user's profile.

Response

{
 "id": "550e8400-e29b-41d4-a716-446655440000",
 "company_name": "Acme Batteries",
 "email": "admin@acme.com",
 "address": "Industrial Area, Delhi",
 "epr_registration_number": "B-29016/2024-25/CPCB",
 "bis_r_number": "R-41001234",
 "created_at": "2026-01-15T10:00:00Z"
}
PUT/auth/profile๐Ÿ” Auth Required

Update the current user's profile and compliance fields.

Request Body

{
 "company_name": "Acme Batteries Inc.",
 "address": "Updated Address, Mumbai",
 "support_email": "support@acme.com",
 "website": "https://acme.com",
 "epr_registration_number": "B-29016/2024-25/CPCB",
 "bis_r_number": "R-41001234",
 "iec_code": "0504012345"
}

Response

{
 "message": "Profile updated successfully"
}

Batches

Create, manage, and export battery batches.

POST/batches๐Ÿ” Auth Required

Create a new battery batch with specifications.

Request Body

{
 "batch_name": "Q1-2026-Production",
 "market_region": "INDIA",
 "specs": {
 "chemistry": "Li-ion NMC",
 "voltage": "48V",
 "capacity": "100Ah",
 "manufacturer": "Acme Batteries",
 "weight": "45kg",
 "country_of_origin": "India"
 },
 "pli_compliant": true,
 "domestic_value_add": 65.5,
 "cell_source": "DOMESTIC"
}

Response

{
 "id": "batch-uuid-here",
 "batch_name": "Q1-2026-Production",
 "created_at": "2026-01-15T10:00:00Z"
}
GET/batches๐Ÿ” Auth Required

List all batches for the authenticated tenant with pagination.

Response

{
 "batches": [
 {
 "id": "batch-uuid-1",
 "batch_name": "Q1-2026-Production",
 "total_passports": 500,
 "market_region": "INDIA",
 "created_at": "2026-01-15T10:00:00Z"
 }
 ],
 "total": 10,
 "page": 1,
 "per_page": 20
}
GET/batches/{id}๐Ÿ” Auth Required

Get detailed information about a specific batch.

Response

{
 "id": "batch-uuid-here",
 "batch_name": "Q1-2026-Production",
 "market_region": "INDIA",
 "specs": { ... },
 "pli_compliant": true,
 "domestic_value_add": 65.5,
 "total_passports": 500,
 "created_at": "2026-01-15T10:00:00Z"
}
POST/batches/{id}/upload๐Ÿ” Auth Required

Upload a CSV file with serial numbers to generate passports.

Request Body

Content-Type: multipart/form-data

file: [CSV file with serial_number,manufacture_date columns]

Response

{
 "message": "Successfully created 500 passports",
 "created": 500,
 "errors": []
}
GET/batches/{id}/download๐Ÿ” Auth Required

Download all QR codes for the batch as a ZIP file.

Response

Content-Type: application/zip
Content-Disposition: attachment; filename="batch-qrcodes.zip"

[Binary ZIP file containing PNG QR codes]
GET/batches/{id}/export๐Ÿ” Auth Required

Export all passports in the batch to a CSV file.

Response

Content-Type: text/csv

uuid,serial_number,manufacture_date,status,qr_url
uuid-1,BAT-001,2026-01-15,ACTIVE,https://example.com/p/uuid-1
GET/batches/{id}/passports๐Ÿ” Auth Required

List all passports in a batch with pagination.

Response

{
 "passports": [
 {
 "uuid": "passport-uuid",
 "serial_number": "BAT-001",
 "manufacture_date": "2026-01-15",
 "status": "ACTIVE"
 }
 ],
 "total": 500,
 "page": 1
}

Passports

Public passport data and scan tracking.

GET/p/{uuid}

Get public passport data. No authentication required.

Response

{
 "uuid": "passport-uuid",
 "serial_number": "BAT-2026-001",
 "manufacture_date": "2026-01-15",
 "status": "ACTIVE",
 "batch": {
 "batch_name": "Q1-2026-Production",
 "market_region": "INDIA",
 "specs": { ... },
 "pli_compliant": true,
 "domestic_value_add": 65.5
 },
 "tenant": {
 "company_name": "Acme Batteries",
 "address": "Industrial Area, Delhi",
 "epr_registration_number": "B-29016/2024-25/CPCB"
 }
}
POST/scan

Record a scan event for a passport. Called automatically when QR is scanned.

Request Body

{
 "passport_id": "passport-uuid"
}

Response

{
 "message": "Scan recorded"
}
GET/scan/feed๐Ÿ” Auth Required

Get real-time scan feed for the authenticated tenant.

Response

{
 "scans": [
 {
 "id": "scan-uuid",
 "passport_id": "passport-uuid",
 "serial_number": "BAT-001",
 "city": "Mumbai",
 "country": "India",
 "device_type": "Mobile",
 "scanned_at": "2026-01-15T14:30:00Z"
 }
 ]
}

Billing

Quota management and Razorpay payment integration.

GET/billing/packages

Get available quota packages for purchase.

Response

{
 "packages": [
 {
 "id": "starter",
 "name": "Starter",
 "quota": 100,
 "price": 999,
 "currency": "INR"
 },
 {
 "id": "professional",
 "name": "Professional",
 "quota": 500,
 "price": 3999,
 "currency": "INR"
 }
 ]
}
GET/billing/quota๐Ÿ” Auth Required

Get current quota balance for the authenticated tenant.

Response

{
 "quota_balance": 450,
 "quota_used": 50,
 "quota_limit": 500
}
POST/billing/razorpay/order๐Ÿ” Auth Required

Create a Razorpay order for quota purchase.

Request Body

{
 "package_id": "professional"
}

Response

{
 "order_id": "order_xyz123",
 "amount": 399900,
 "currency": "INR",
 "key_id": "rzp_test_xxx"
}
POST/billing/razorpay/verify๐Ÿ” Auth Required

Verify Razorpay payment and add quota.

Request Body

{
 "razorpay_order_id": "order_xyz123",
 "razorpay_payment_id": "pay_abc456",
 "razorpay_signature": "signature_here"
}

Response

{
 "message": "Payment verified, quota added",
 "new_balance": 950
}

Dashboard

Statistics and analytics endpoints.

GET/dashboard/stats๐Ÿ” Auth Required

Get dashboard statistics for the authenticated tenant.

Response

{
 "total_passports": 2500,
 "total_batches": 15,
 "quota_used": 2500,
 "quota_limit": 5000,
 "passports_this_week": 340,
 "pending_export_batches": 2,
 "carbon_compliance_pct": 85.5
}
GET/dashboard/recent-batches๐Ÿ” Auth Required

Get the most recent batches for quick access.

Response

{
 "batches": [
 {
 "id": "batch-uuid",
 "batch_name": "Q1-2026-Production",
 "total_passports": 500,
 "status": "ACTIVE",
 "created_at": "2026-01-15T10:00:00Z"
 }
 ]
}

Passport Lifecycle

Track and manage passport status transitions with tamper-proof audit trail.

POST/passports/{uuid}/transition๐Ÿ” Auth Required

Apply a lifecycle action with automatic event logging.

Request Body

{
 "action": "SHIP",
 "evidence": {
 "shipment_reference": "IND123456789",
 "shipment_date": "2026-01-15"
 }
}

Response

{
 "success": true,
 "previous_status": "ACTIVE",
 "new_status": "SHIPPED",
 "action": "SHIP",
 "event_id": "event-uuid"
}
GET/passports/{uuid}/transitions๐Ÿ” Auth Required

Get allowed lifecycle actions for a passport.

Response

{
 "current_status": "SHIPPED",
 "allowed_actions": ["INSTALL", "RECALL"]
}
GET/passports/{uuid}/events๐Ÿ” Auth Required

Get audit trail with tamper-proof hash chain.

Response

{
 "events": [
 {
 "id": "event-uuid",
 "event_type": "STATUS_CHANGED",
 "action": "SHIP",
 "actor": "admin@acme.com",
 "actor_role": "LOGISTICS",
 "status_from": "ACTIVE",
 "status_to": "SHIPPED",
 "metadata": { "evidence": { "shipment_reference": "IND123456789" } },
 "hash": "sha256:abc123...",
 "previous_hash": "sha256:def456...",
 "created_at": "2026-01-15T10:00:00Z"
 }
 ]
}
POST/passports/bulk/transition๐Ÿ” Auth Required

Bulk transition multiple passports.

Request Body

{
 "passport_ids": ["uuid-1", "uuid-2", "uuid-3"],
 "action": "SHIP",
 "evidence": {
 "shipment_reference": "BATCH-2026-001",
 "shipment_date": "2026-01-15"
 }
}

Response

{
 "total": 3,
 "succeeded": 3,
 "failed": 0,
 "failed_ids": [],
 "errors": []
}

Team Management

Invite team members and manage roles within your organization.

GET/team๐Ÿ” Auth Required

List all team members for the tenant.

Response

{
 "members": [
 {
 "id": "user-uuid",
 "email": "operator@acme.com",
 "role": "operator",
 "status": "active",
 "invited_at": "2026-01-10T10:00:00Z"
 }
 ]
}
POST/team/invite๐Ÿ” Auth Required

Invite a new team member via email.

Request Body

{
 "email": "newuser@acme.com",
 "role": "operator"
}

Response

{
 "message": "Invitation sent",
 "invite_id": "invite-uuid"
}
PUT/team/{id}/role๐Ÿ” Auth Required

Update a team member's role.

Request Body

{
 "role": "admin"
}

Response

{
 "message": "Role updated"
}
DELETE/team/{id}๐Ÿ” Auth Required

Remove a team member.

Response

{
 "message": "Team member removed"
}

Webhooks

Server-side webhook handlers for secure payment processing.

๐Ÿ”’ Secure Payment Processing

Webhooks use HMAC-SHA256 signature verification. Quota is ONLY added via webhook confirmation, not client-side verification.

POST/webhooks/razorpay

Razorpay payment webhook. Verifies signature and adds quota on payment.captured.

Request Body

Headers:
X-Razorpay-Signature: <HMAC-SHA256 signature>

Body:
{
 "event": "payment.captured",
 "payload": {
 "payment": {
 "entity": {
 "id": "pay_abc123",
 "order_id": "order_xyz789",
 "amount": 399900
 }
 }
 }
}

Response

{
 "status": "ok"
}

// Or on signature failure:
HTTP 401 Unauthorized

Error Responses

All error responses follow a consistent format:

{
 "error": "Error message describing what went wrong",
 "code": "ERROR_CODE"
}
400Bad Request - Invalid input data
401Unauthorized - Missing or invalid token
403Forbidden - Insufficient permissions
404Not Found - Resource does not exist
500Internal Server Error