Portals

Supplier Portal Broker Portal Carrier Portal Customer Portal Mobile app

Integrations

All integrations API docs

Resources

Guides Blog Glossary FAQ Security Pricing About Us Login Request a Demo

API Reference

Energy Connect Public API v1 — RESTful JSON over HTTPS

Download the current OpenAPI contract · Explorer · Integration guide

Last updated

Quickstart

Base URL: https://api.energyconnecter.ai/v1/public

The API covers 135 endpoints across 17 resource groups: Orders (47), Pricing (14), Connections & Freight Rates (12), Invoices (12), Companies (9), Webhooks (8), Locations (7), Supplier Terminals (5), Tax Rates (5), API Keys (4), Allocations (3), Load Board (3), Payments (2), and one each for Customers, Suppliers, Product Taxonomy and Reference Data. The browser below is rendered client-side; for a static list of every operation see /llms-full.txt.

Authentication

Pass your API key either as an X-API-Key header or as an Authorization: Bearer token — both are accepted. Keys start with ec_live_ (production) or ec_test_ (sandbox: isolated test company). A test key never reads or changes live data, sends no email or SMS, is not billed and has its own monthly cap; once authenticated, its responses carry X-EC-Sandbox: true.

curl -H "X-API-Key: ec_live_your_key_here" \
  https://api.energyconnecter.ai/v1/public/orders

# equivalently, as a Bearer token:
curl -H "Authorization: Bearer ec_live_your_key_here" \
  https://api.energyconnecter.ai/v1/public/orders

Idempotency

Every write (POST, PUT, PATCH, DELETE) requires an Idempotency-Key header — 16–200 characters of A-Za-z0-9_-. A UUID works. Omit it and the call returns 400 IDEMPOTENCY_KEY_REQUIRED. The only exception is POST /pricing/freight-calculate, which computes and writes nothing.

Use a fresh key for every distinct request. Retrying with the same key and the same body replays the original response — marked Idempotency-Replayed: true — without re-running side effects such as dispatch SMS or email. Records are kept for 24 hours. Reusing a key with a different body returns 409 IDEMPOTENCY_KEY_CONFLICT; a key whose original request is still in flight returns 409 IDEMPOTENCY_IN_FLIGHT with Retry-After. A key whose request ended in a 5xx returns 409 IDEMPOTENCY_OUTCOME_UNKNOWN: the write may have completed, so check the resource rather than resubmitting. After a 504 REQUEST_TIMEOUT the key is held for the full 24 hours; after any other 5xx, for 60 seconds (Retry-After gives the time left), and then a retry runs. After a 4xx the key is free at once, so you can fix the request and retry it.

curl -X POST https://api.energyconnecter.ai/v1/public/delivery-tickets \
  -H "X-API-Key: ec_live_your_key_here" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"bolNumbers":["1234567"]}'

Rate Limits

600 requests/min burst
50,000 billable requests/month
RateLimit-* headers on every response; Retry-After on 429

Response Format

Success: { success: true, data: ... }
List: { success: true, data: [...], meta: { total, page, pageSize, totalPages, hasMore } }
Error: { success: false, error: { code, message } }

Pagination

Use ?page=1&pageSize=20 query params.
Max pageSize is 100 (500 for connections).
To sync only what changed, GET /orders, /invoices and /delivery-tickets take ?updatedSince=: walk with meta.nextCursor passed back as cursor until hasMore is false.

Errors

400 VALIDATION_ERROR • 401 INVALID_API_KEY
403 INSUFFICIENT_SCOPE • 404 NOT_FOUND
429 RATE_LIMIT_EXCEEDED (error.kind: burst = retry soon, quota = monthly cap, wait for resetAt)

Versioning & deprecation

Within /v1 the contract only grows. An integration built against it keeps working, unchanged, for the life of /v1.

What v1 guarantees

  • No endpoint, field or enum value is ever removed or renamed in place. A renamed concept keeps its old key as a permanent alias (gallons/unit beside quantity/unitCode).
  • We may add endpoints, optional request fields, response fields and enum values. Ignore fields you don't recognise and tolerate unknown enum values.
  • Also treated as breaking, so they do not happen in v1: changing a field's type or meaning, tightening validation of existing input, changing a default (sort, page size, filter), changing the status code for an existing situation, making an always-present field nullable, and adding a required request field.

When a breaking change is needed

It ships as /v2/public, running beside v1. v1 stays up at least 12 months after v2 is generally available, and at least 6 months after the last active v1 caller migrates. Retirement is announced, then dual-run, then scheduled and announced brownouts (short 410 windows), then 410 Gone pointing to the migration guide. Nothing is removed silently.

Notice periods

ChangeNotice
Additive changeChangelog entry
Rate limits, quotas, IP policy30 days
Deprecation of v112 months before sunset
Brownout windowsListed in the deprecation notice; reminders 30 and 7 days before each
Emergency security fixDirect contact, before or at rollout

Notices go to the creator of each affected active key and to the company's primary contact, name a person to reply to, and are recorded in the changelog below. A fix for behavior that contradicted this documentation can ship within v1 and is listed there too.

Changelog

  • — Order lines add productCode (product master id, e.g. DSL-ULSD-2) and terminalCode (IRS terminal control number, as on GET /master-terminals). Exact matches only: null when uncertain, so fall back to the name.
  • — Allocations: rows add brand ("" = unbranded) and supplierId (granting supplier; null on your own row), so rows sharing terminal, product and period differ.
  • — Idempotency: a write that ends in a 5xx no longer releases its Idempotency-Key. A retry with the same key gets the new 409 IDEMPOTENCY_OUTCOME_UNKNOWN: for 24 hours after a 504 REQUEST_TIMEOUT, which can still complete, and for 60 seconds after any other 5xx, then the retry runs. A retry used to run a timed-out write a second time. A 4xx still frees the key at once.
  • — Invoices, invoice amendments and payments: a company that is neither the issuer nor the payer gets 404, the same as an unknown id, instead of 403. A payer reading an unissued draft by id also gets 404. A party acting outside its role still gets 403.
  • — Price imports: committing a batch creates new supplier terminals for unmapped terminal codes only when the key also holds supplier_terminals:write. With pricing:write alone, those rows are skipped and their codes are listed in the new unresolvedTerminalCodes response field. Map them with /pricing/imports/terminal-aliases and import again.
  • — Locations: a schedule day marked open must carry both openTime and closeTime. A closed day needs neither.
  • — Locations: an empty contacts, schedules or compartments array returns 400 instead of deleting every row. Omit the field to leave it unchanged; set isActive: false to stand a site down.
  • — PUT on orders, locations, companies and supplier terminals is genuinely partial: fields you don't send are left unchanged. Schema defaults used to overwrite them.
  • — Order lines accept any unit in the units-of-measure catalog and fractional quantities. gallons/unit remain permanent aliases.
Browse endpoints