mytmn-hub

REST API that fetches, normalizes, and caches MyTaman billing data as structured JSON. Integrate using the endpoints below; use the machine-readable OpenAPI spec or Postman collection for your stack.

REST JSON OpenAPI 3.0 Bearer auth

Quick start

1. Authenticate with your MyTaman credentials to get a session token:

# Login
curl -X POST https://mytmn.sofehaus.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"s3cret"}'

# Response: {"token":"c0a80164-..."}

2. Use the token for all subsequent requests:

# Get your taman profile
curl https://mytmn.sofehaus.com/api/profile \
  -H "Authorization: Bearer <token>"

# List all invoice profiles
curl https://mytmn.sofehaus.com/api/billing/invoices \
  -H "Authorization: Bearer <token>"

# Get stats for a specific invoice
curl https://mytmn.sofehaus.com/api/billing/invoices/9978/stats \
  -H "Authorization: Bearer <token>"

3. Logout when done (invalidates token + clears cache):

curl -X POST https://mytmn.sofehaus.com/api/auth/logout \
  -H "Authorization: Bearer <token>"
Cache: Responses are cached in-memory per session — profile (30 min), invoice list (10 min), stats (5 min). Logout evicts all cached data for that session.

API reference

GET /health

Service health check. No authentication required.

// 200 OK
{ "ok": true, "service": "mytmn-hub" }

POST /api/auth/login

Authenticate with MyTaman. Returns a session token (UUID). The token maps to the upstream session cookie server-side.

Body FieldTypeRequiredDescription
emailstringYesMyTaman email
passwordstringYesMyTaman password
// 201 Created
{ "token": "c0a80164-1234-5678-9abc-def012345678" }
StatusMeaning
201Token created
400Invalid JSON or missing email/password
401MyTaman rejected credentials

POST /api/auth/logout Bearer

Destroy session and evict all cached data for this token. Returns 204 regardless of token validity.

GET /api/profile Bearer

Taman name and address parsed from MyTaman navigation bar. Cached 30 min.

TamanInfo

namestringTaman name
addressstringPrimary house address
// 200 OK
{ "name": "Taman Bukit Indah", "address": "No 1, Jalan Bukit Indah 1/1" }

GET /api/billing/invoices Bearer

All invoice profiles from the dashboard dropdown. Cached 10 min.

InvoiceProfileOption[]

idintegerInvoice profile ID
namestringDisplay name
selectedbooleanCurrently active profile
// 200 OK
[
  { "id": 9978, "name": "Monthly Maintenance", "selected": true },
  { "id": 10234, "name": "Sinking Fund", "selected": false }
]

GET /api/billing/invoices/:id/stats Bearer

Billing statistics for one invoice profile. Cached 5 min. The :id is an invoice_profile_id from the list above.

CurrentInvoiceProfile

idintegerInvoice profile ID
titlestringInvoice title
frequencystringe.g. "Monthly", "One Off"
startDatestringBilling start date
totalIssuedBillTotal{count, amount}
totalCollectedBillTotal{count, amount}
totalOutstandingBillTotal{count, amount}
paidHousesobject{paid: int, unpaid: int}
// 200 OK
{
  "id": 9978,
  "title": "Monthly Maintenance",
  "frequency": "Monthly",
  "startDate": "2024-01-01",
  "totalIssued": { "count": 271, "amount": 21680.00 },
  "totalCollected": { "count": 258, "amount": 19820.00 },
  "totalOutstanding": { "count": 13, "amount": 1860.00 },
  "paidHouses": { "paid": 279, "unpaid": 18 }
}

GET /api/billing/dashboard Bearer

Full composite dashboard: taman info + all invoice profiles + current profile stats. Optional ?profile_id= to load a specific profile. Cached 5 min.

Query ParamTypeRequiredDescription
profile_idintegerNoLoad specific invoice profile
// 200 OK
{
  "taman": { "name": "...", "address": "..." },
  "profiles": [ ... ],
  "current": { ... }
}

Error handling

All errors return JSON with an error code and optional detail.

ErrorResponse

errorstringMachine-readable error code
detailstring?Optional human-readable context
StatusError CodeWhen
400invalid_jsonRequest body is not valid JSON
400invalid_bodyMissing required fields or invalid path param
401unauthorizedMissing / invalid / expired Bearer token
401login_failedMyTaman rejected credentials or session expired
404not_foundUnknown route
502upstream_errorMyTaman returned non-200
500internal_errorUnexpected server error

Caching strategy

Responses are cached in-memory per session token. This reduces load on MyTaman while keeping data reasonably fresh.

ResourceCache KeyTTL
Taman profile{token}:profile30 min
Invoice list{token}:invoices10 min
Invoice stats{token}:invoice:{id}5 min
Full dashboard{token}:dashboard:{id|default}5 min
Invalidation: Logging out (POST /api/auth/logout) evicts all cached entries for that session token. Cache entries also self-expire via lazy TTL check on read.

Architecture

How a protected read flows through the hub: session token resolves to a MyTaman cookie; repositories consult the TTL cache before touching upstream.

New MyTaman screens follow the same pattern: dedicated fetch module + parser + repository slice, sharing the global CacheStore and session cookie from login.

Release notes

API Changelog below describe changes that may affect your integration (HTTP surface, auth, JSON fields, caching visible to clients). Fetch GET /release-notes for this same content over the wire.

Release notes are also available in the repo under CHANGELOG.md

Downloads & tools

Postman tip: Import the collection, set the baseUrl variable to this API’s base URL, and run Auth > Login first — the test script auto-saves the token for all other requests.