For ICT · product · waste operations

CouncilBins integration API

Recommended interface for councils that want to keep addresses, collection rules, and holiday exceptions in sync with their own systems — without reshaping how they already store bin data.

REST · JSON Versioned API keys + optional mTLS v1 recommendation

Overview

CouncilBins is the resident-facing progressive web app. The integration API is how your council (or a systems integrator) feeds operational data into that experience — and optionally receives issue reports back into your CRM or service desk.

Every council stores collection data differently. This guide describes the recommended capability surface WM Studios implements on client projects. Endpoints can be mapped to your property system, waste contractor feeds, GIS exports, or middleware.

Not locked to one source system. You can start with bulk upload or managed updates, then graduate to API sync when ready. The resident app does not change.

Who this is for

  • Council ICT / digital — evaluating integration effort and security posture
  • Waste & cleansing operations — confirming what data must stay current
  • Vendors & integrators — property, CRM, or waste platforms connecting outbound/inbound

The interactive demo at /Demo/ shows the resident experience. This page describes the recommended production integration, not the demo’s simplified endpoints.

Design principles

  1. Your identifiers win — use your property/assessment IDs as external keys; we store a mapping.
  2. Push or pull — you can POST changes to us, or we can pull from a feed you expose (by agreement).
  3. Idempotent writes — safe retries with the same payload do not create duplicates.
  4. Partial updates — update a zone, a holiday table, or a suburb batch without full reloads.
  5. Auditability — imports and API writes are logged with actor, timestamp, and summary counts.

Authentication

Production APIs are not public. Typical setup:

MethodUse when
Authorization: Bearer <api_key> Server-to-server from council middleware or iPaaS
IP allowlisting Fixed egress from council data centre / cloud NAT
Optional mutual TLS Higher assurance environments (by request)

Keys are issued per environment (sandbox / production) and can be rotated without downtime. Resident devices never hold integration keys — they only talk to the public app APIs.

curl -sS \
  -H "Authorization: Bearer cb_live_***" \
  -H "Accept: application/json" \
  https://{tenant}.councilbins.example/api/v1/health

Base URL & versioning

https://{tenant-host}/api/v1/
  • Tenant host — dedicated hostname or path for your council instance
  • Version — breaking changes ship under a new version prefix; v1 remains stable for the contract term
  • Format — JSON request/response bodies; UTF-8; dates as YYYY-MM-DD; times as ISO-8601 where needed
  • Timezone — collection logic uses the council timezone configured at go-live (e.g. Australia/Sydney)

Resource map

AreaCapabilityDirection
Addresses / propertiesUpsert, search, deactivateCouncil → CouncilBins
Zones & rulesCollection day, bin cadence, FOGO flagsCouncil → CouncilBins
ExceptionsPublic holidays & alternate datesCouncil → CouncilBins
Recycling guideWhat-goes-where catalogueCouncil → CouncilBins
News / tipsIn-app contentCouncil → CouncilBins
ReportsMissed bins, dumping, damaged binsCouncilBins → Council
Bulk jobsFile or JSON batch import statusBoth

Addresses

An address is what a resident searches for. It must resolve to a collection zone (or explicit day + rules) so the app can build a personalised schedule.

Upsert address

PUT /api/v1/addresses/{external_id}

{
  "external_id": "PROP-1002847",
  "address_line_1": "12 Riverbank Drive",
  "suburb": "Riverside",
  "postcode": "2150",
  "state": "NSW",
  "zone_code": "ZONE-A",
  "collection_day": "Monday",
  "active": true,
  "meta": {
    "assessment_number": "A-884201",
    "latitude": -33.8688,
    "longitude": 151.2093
  }
}

Batch upsert

POST /api/v1/addresses:batchUpsert

{
  "items": [ { "external_id": "PROP-1", "address_line_1": "…", "zone_code": "ZONE-A", "…" : "…" } ],
  "on_missing_zone": "reject"
}

Recommended batch size: up to 1,000 items per request (or use bulk upload for full estate refreshes).

Deactivate

POST /api/v1/addresses/{external_id}/deactivate — hides from resident search without destroying history.

Schedules & zones

Zones encode the rules residents experience: which weekday, which bins alternate, whether FOGO is weekly, etc. Addresses reference a zone_code so zone changes cascade without rewriting every property.

Upsert zone

PUT /api/v1/zones/{zone_code}

{
  "zone_code": "ZONE-A",
  "name": "Riverside north",
  "collection_day": "Monday",
  "bins": {
    "garbage":   { "frequency": "weekly" },
    "recycling": { "frequency": "fortnightly", "parity": "even_iso_week" },
    "green":     { "frequency": "fortnightly", "parity": "odd_iso_week" },
    "fogo":      { "frequency": "weekly" }
  },
  "notes": "Optional operations note"
}

Preview schedule (optional)

GET /api/v1/addresses/{external_id}/schedule?weeks=8

Returns the same shape the resident app uses — useful for UAT and for operations to verify a property after a zone change.

{
  "address": { "external_id": "PROP-1002847", "display": "12 Riverbank Drive, Riverside 2150" },
  "schedule": [
    {
      "iso_date": "2026-07-20",
      "bins": [
        { "type": "garbage", "label": "General Waste" },
        { "type": "recycling", "label": "Recycling" },
        { "type": "fogo", "label": "FOGO" }
      ],
      "note": null
    }
  ]
}

Collection exceptions

Public holidays and one-off shifts. When a normal collection day hits an exception date, the app shows the alternate date or skip reason.

PUT /api/v1/exceptions/{exception_date}

{
  "exception_date": "2026-12-25",
  "reason": "Christmas Day — delayed collection",
  "alternate_date": "2026-12-27",
  "applies_to": "all_zones"
}

applies_to may be all_zones or an array of zone codes. Bulk replace for a year is supported via POST /api/v1/exceptions:replaceYear with a full list.

Recycling guide

Optional if you maintain a what-goes-where catalogue. Items power search and filters in the app.

POST /api/v1/recycling/items:batchUpsert

{
  "items": [
    {
      "external_id": "REC-PLASTIC-PET",
      "name": "Plastic bottles (PET/HDPE)",
      "category": "Plastics",
      "bin_type": "recycling",
      "description": "Empty, rinse, lids loosely on.",
      "keywords": ["plastic", "bottle", "pet"]
    }
  ]
}

Allowed bin_type values (configurable per council): recycling, garbage, green, fogo, special.

Reports (outbound to council)

When a resident submits a missed bin, damaged bin, illegal dumping, or other issue, CouncilBins stores a reference and can deliver the payload to you.

ModeHow it works
Webhook HTTPS POST to your endpoint on submit (recommended)
Pull API GET /api/v1/reports?since=… for polling integrations
Email / shared mailbox Available for pilots; not preferred long-term
{
  "event": "report.created",
  "id": "rpt_01J…",
  "reference": "CB-A628-9166",
  "type": "missed_bin",
  "details": "Red bin not emptied; left out from 6am.",
  "address": {
    "external_id": "PROP-1002847",
    "display": "12 Riverbank Drive, Riverside 2150"
  },
  "contact": {
    "name": "Alex Resident",
    "phone": "04xx xxx xxx",
    "email": null
  },
  "created_at": "2026-07-18T21:15:00+10:00"
}

Bulk upload API

For estate-wide refreshes or when a nightly file is simpler than fine-grained REST calls. Same validation rules as the interactive API; results are tracked as a job.

POST /api/v1/imports (multipart)

  • Formats — CSV or JSON (schema provided at onboarding)
  • Typesaddresses, zones, exceptions, recycling
  • Modemerge (default) or replace_scope (e.g. replace all addresses in a suburb)
curl -X POST https://{tenant-host}/api/v1/imports \
  -H "Authorization: Bearer cb_live_***" \
  -F "type=addresses" \
  -F "mode=merge" \
  -F "file=@addresses_2026-07-18.csv"

GET /api/v1/imports/{job_id}

{
  "id": "imp_01J…",
  "type": "addresses",
  "status": "completed",
  "received": 42810,
  "upserted": 42792,
  "rejected": 18,
  "error_report_url": "https://…/imports/imp_01J…/errors.csv"
}

Webhooks

  • Signed with an HMAC secret (X-CouncilBins-Signature)
  • At-least-once delivery with exponential retry (e.g. 1m / 5m / 30m / 2h)
  • Your endpoint should respond 2xx quickly and process asynchronously if needed
  • Idempotency key included so duplicate deliveries can be ignored

Configurable events include report.created, import.completed, and import.failed.

Errors, pagination & limits

Error shape

{
  "status": "error",
  "code": "validation_failed",
  "message": "zone_code ZONE-Z does not exist",
  "details": [
    { "field": "zone_code", "issue": "unknown_zone" }
  ],
  "request_id": "req_01J…"
}
HTTPMeaning
400 / 422Validation or business rule failure
401 / 403Missing or invalid credentials
404Unknown resource
409Conflict (e.g. concurrent replace)
429Rate limited — back off using Retry-After
5xxTransient platform error — safe to retry idempotent calls

List endpoints support cursor pagination (?cursor= / next_cursor). Default rate guidance: 120 requests/minute per key for interactive APIs; bulk imports are separate and queued.

Integration checklist

  1. Confirm source of truth for addresses (property system, waste contractor, GIS).
  2. Define zone model: day + bin cadence (+ FOGO if applicable).
  3. Agree external ID strategy (stable property key).
  4. Map public holiday process for the next 12–24 months.
  5. Choose report delivery: webhook URL or pull API (+ CRM field mapping).
  6. Sandbox credentials, sample payloads, and UAT address set.
  7. Go-live: cutover plan, monitoring contacts, key rotation owner.

Get a tailored contract

Endpoint names and fields above are the recommended baseline. On engagement we publish your tenant’s OpenAPI (Swagger) document, sandbox keys, and sample files mapped to your systems.

Phone 0447 599 119 · WM Studios · or use the form on the product site