Idempotency & quotas¶
Write endpoints (POST) and the RPA tarification endpoint apply safeguards so that retries,
network glitches and accidental loops never produce duplicates or runaway costs.
Idempotency-Key¶
Send an Idempotency-Key header on every POST. The same key + same cabinet + same endpoint
returns the same result instead of creating a second resource.
curl -X POST https://dev.aeliam.ai/api/v1/public/opportunities \
-H "X-API-Key: aelm_dev_xxxxxxxx" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{"title":"New lead","product":"mrp","contact":{"last_name":"Martin"}}'
- A repeated key returns the original response with HTTP 200 (idempotent replay) instead of 201.
- Two truly concurrent requests with the same key may yield 409
idempotency_conflict— retry the same request. - Use a fresh UUID per distinct operation.
Cursor pagination¶
List endpoints are keyset-paginated with an opaque, HMAC-signed cursor:
limit: 1–100 (default 50), clamped server-side.- Pass
next_cursorback as?cursor=…. Never craft or mutate a cursor — it is signed and will be rejected. - Cursors are deterministic (by
created_at+id), so they stay valid as new data arrives.
RPA tarification — quotas & anti-abuse¶
POST /automation/tarifications triggers a real, billable robot run. Extra guards apply:
error (HTTP) |
Meaning | What to do |
|---|---|---|
rpa_quota_exceeded (429) |
Cabinet hit its hourly tarification quota | Wait until reset_at |
rpa_replay_detected (429) |
Identical dicData submitted again too soon |
Don't loop; wait the indicated delay |
rpa_circuit_open (503) |
Too many failures on that insurer — breaker open | Retry later |
api_rpa_temporarily_disabled (503) |
Global kill-switch is on | Retry later |
dry_run first¶
Validate the whole pipeline (schema, quota, credentials) without creating a job or incurring cost:
curl -X POST https://dev.aeliam.ai/api/v1/public/automation/tarifications \
-H "X-API-Key: aelm_dev_xxxxxxxx" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"compagnie":"axa","produit":"mrp","dry_run":true,
"dicData":{"raison_sociale":"Acme","siret":"00000000000000"}}'
{
"job_id": null,
"status": "dry_run_ok",
"validation": {
"schema_ok": false,
"missing_required_fields": ["chiffre_affaires"],
"quota": { "remaining": 95, "limit": 100 },
"credentials_configured": true,
"would_cost_credits": 5
},
"credits_consumed": 0
}
Cost model (observe mode)¶
Today, tarifications are not billed. The platform records a nominal cost (~5 credits per
tarification) in observe mode — no debit, no blocking. The credits_consumed / billing_mode:
"observe" fields are informational while pricing is finalised.
Job lifecycle¶
After a real enqueue you get a job_id. Poll GET /automation/jobs/{job_id}:
{
"job_id": "…",
"status": "completed",
"compagnie": "axa",
"produit": "mrp",
"devis_id": "…",
"result": {
"tarif_eur": 376.65,
"suspens": "0000012345678901",
"pdf_url": "/api/v1/public/devis/<devis_id>/pdf"
},
"progress": { "step": "ack:completed", "percent": 100 }
}
Statuses: pending → claimed → running → (mfa_required / awaiting_mfa) → completed
| partial | failed | cancelled. Provide a callback_webhook_id to be notified instead of
polling — see Webhooks.