V8 — first inbound-side skill. Closes the loop from "bill arrives by email"
to "ready to enter in Dolibarr UI". Read-only at every layer.
What ships:
- arcodange-email-ingest/scripts/zoho-curl.sh OAuth wrapper with token cache
(50 min TTL, mode 600) — avoids
hitting Zoho OAuth rate limit on
every invocation.
- arcodange-email-ingest/scripts/email-list.sh List candidates in /Inbox/books
(where the books@ alias auto-
routes mail). --candidates-only
filter on supplier patterns or
attachments. --all-folders to
scan everything.
- arcodange-email-ingest/scripts/email-inspect.sh Pull message + attachments,
pdftotext on each PDF, heuristic
extract (supplier, ref, dates,
totals, VAT rate), emit Dolibarr
supplier-invoice draft JSON.
Architecture choice — Zoho API (not IMAP):
- books@arcodange.fr is an alias of gabrielradureau@arcodange.fr → one OAuth
refresh_token covers everything.
- Gmail folded in via forwarding (arcodange@gmail.com → books@) — no Google
API setup, no app-passwords, no second OAuth flow.
- Token-based auth, no SCA rabbit hole.
V8.0 baseline (in /Inbox/books):
- 3 candidates: Mistral AI facture, Anthropic Stripe receipt (Fwd Gmail),
INPI payment receipt (Fwd Gmail).
- Heuristic extraction is best-effort: works on amounts/refs for some
templates, misses others (Mistral PDF format, Stripe receipt layout).
- --save-pdf <DIR> lets the operator grab the PDFs for manual entry when
the heuristic falls short.
Rate-limit pitfall documented: Zoho OAuth refresh has an aggressive throttle
("too many requests continuously"). The cache file at $TMPDIR/zoho-access-$USER
(mode 600, 50 min TTL) prevents this; on 401 the wrapper auto-refreshes once
and retries.
V8.1+ ideas in SKILL.md out-of-scope:
- mark ingested emails (IMAP flag or Zoho label)
- body text extraction (inline-HTML invoices)
- per-template parsers or LLM-based extraction
- IMAP fallback for non-Zoho mailboxes
CLI: bin/arcodange email {list|inspect|curl} integrated.
Base updates: dolibarr/SKILL.md cross-link, dolibarr/README.md env schema
extended with ZOHO_CLIENT_ID/SECRET/REFRESH_TOKEN/DC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 KiB
name, description, requires
| name | description | requires | |||||||
|---|---|---|---|---|---|---|---|---|---|
| dolibarr | Reference + connection layer for the Arcodange Dolibarr ERP REST API at https://erp.arcodange.lab/. Documents authentication via DOLAPIKEY, the read-only `ai_agent` account's permission requirements (the `voir_tous` ACL trap that returns empty arrays or 404s instead of 403s on listing endpoints), the credentials layout at `.claude/skills/dolibarr/.env`, the `scripts/dol-curl.sh` wrapper, the catalogue of useful read endpoints (invoices, thirdparties, documents/download, products, status, users/info), and the common gotchas (mode-as-int, paye-vs-status, empty-vs-404-vs-403, unix-epoch dates). Use when setting up the connection, debugging an API call, looking up an endpoint, decoding a permission error, or as a dependency referenced by any `dolibarr-*` workflow skill. SKIP for Dolibarr admin / Helm chart / Ansible config (covered by `chart/` + `ansible/`), for any ERP other than Arcodange's, and for specific business workflows that have their own `dolibarr-<topic>` skill. |
|
dolibarr — Arcodange ERP API platform reference
This is the base skill for talking to the Arcodange Dolibarr instance from Claude Code. It documents the connection, the permissions, the endpoint catalogue, and the gotchas. Each business workflow lives in its own dolibarr-<topic> sibling skill that depends on this one — V1 ships dolibarr-invoice-audit for the KissMetrics audit; future siblings will follow the same pattern (e.g. dolibarr-payments-state, dolibarr-tva-reconciliation).
The API key for ai_agent is read-only by design. Never attempt writes from any dolibarr-* skill — use the Dolibarr UI directly for corrections.
Quick start
From the repo root:
bin/arcodange whoami | jq -r .login # → ai_agent
bin/arcodange ping # → {"success":{"code":200,"dolibarr_version":"22.0.4",...}}
bin/arcodange curl /invoices/12 # raw read-only call
The bin/arcodange CLI is the human entry point; under the hood it delegates to the same per-skill scripts. Direct script access still works:
./.claude/skills/dolibarr/scripts/dol-curl.sh /users/info | jq -r .login
If you see ai_agent, auth works. If not, check .env (next section) and dol-curl.sh's error message.
For a liveness check that needs no auth:
curl -s https://erp.arcodange.lab/api/index.php/status | jq .
# → {"success":{"code":200,"dolibarr_version":"22.0.4",...}}
Credentials
Schema of .claude/skills/dolibarr/.env (mode 600, gitignored):
DOLIBARR_URL=https://erp.arcodange.lab
DOLIBARR_API_KEY=<rotatable api key for ai_agent>
DOLIBARR_USER=ai_agent
DOLIBARR_PASSWORD=<used only for browser/UI login, very rarely>
The DOLAPIKEY is the only secret needed for API calls. DOLIBARR_USER/DOLIBARR_PASSWORD are kept here only so the operator has them at hand if they need to log into the Dolibarr UI to fix permissions.
Rules:
chmod 600 .env. Verify withstat -f %Lp .env→600.- Never commit. The root
.gitignorealready excludes.envat any depth; redundant patterns (*.credentials,.claude/skills/**/.env) belt-and-braces it. - Never paste the key into chat. If a key leaks, rotate it from the Dolibarr UI:
Users → ai_agent → API key → Generate new. - Every sibling
dolibarr-*skill sources this file via../dolibarr/.env. There is exactly one copy.
Permission gotcha — read this first
Dolibarr enforces TWO layers of access:
- Per-API-endpoint permissions (does the user's role allow
/invoices?). - Per-record ACL (can this user see THIS invoice/thirdparty?).
Layer 2 is the trap. When ai_agent has the endpoint permission but not the voir_tous flag, listing endpoints silently filter rows out:
GET /invoicesreturns[]with HTTP 200 (looks like "no invoices exist").GET /thirdpartiesreturns HTTP 404 with"No third parties found"(looks like "no thirdparties exist").GET /thirdparties/{id}returns HTTP 403 with"Access not allowed for login ai_agent on this thirdparty"(this is the only honest error).
Diagnostic recipe. If a listing endpoint returns empty/404 while you expect data:
./scripts/dol-curl.sh /thirdparties/1
If you get a 403 with Access not allowed, the ACL is the cause (saved as a reference at examples/acl_403_thirdparty.json). Do not retry the call blindly. Fix the permissions, then retest.
Fix. In the Dolibarr UI (https://erp.arcodange.lab/ → Setup → Users & Groups → ai_agent → Permissions), tick:
- Tiers → Lire les tiers
- Tiers → Voir tous les tiers (et pas seulement ceux liés à l'utilisateur courant)
- Factures → Lire les factures
- Factures → Voir toutes les factures (et pas seulement celles liées à l'utilisateur courant)
Add the equivalent flags for any new module a future dolibarr-* skill needs (Paiements, Produits, …). The ai_agent user has no UI access by default, so voir_tous is the right grant — there's no sales-rep relationship to honor.
Endpoint catalogue
Read-only endpoints we've validated against this instance. Live captures are under examples/.
| Endpoint | HTTP | Purpose | Key params / gotchas | Example |
|---|---|---|---|---|
GET /status |
200 | Liveness + Dolibarr version | No auth needed | status.json |
GET /users/info |
200 | Current API user (auth sanity) | Returns login, id, rights tree |
users_info.json |
GET /invoices |
200 | List invoices | limit, sortfield=t.datef, sortorder=DESC, status=draft|unpaid|paid, sqlfilters |
invoices_list.json |
GET /invoices/{id} |
200 | Invoice detail | paye=1 is the canonical "paid" flag |
invoice_detail.json |
GET /invoices/{id}/payments |
200 | Payments applied to one invoice | Returns array of {amount, type, date, num, ref, fk_bank_line}. date is a string YYYY-MM-DD HH:MM:SS, NOT unix epoch like elsewhere. Amounts negative for AVOIRs. |
invoices_12_payments.json |
GET /payments |
501 | (list-all not implemented) | Returns "API not found". To enumerate payments, iterate invoices. | — |
GET /bankaccounts / /{id} |
200 | List bank accounts / detail | Returns ref, label, iban, country_code. fk_bank_line on a payment doesn't directly map to the account id — see dolibarr-payments-state skill for the lookup. |
bankaccounts_list.json |
GET /thirdparties |
200 | List thirdparties | mode MUST be integer (mode=1 = customers). String fails 400. |
(use /{id} below) |
GET /thirdparties/{id} |
200 | Thirdparty detail | KissMetrics is id=1 in this instance |
thirdparty_km.json |
GET /products / /products/{id} |
200 | Product catalogue | KM-audit is id=1, KM-cloud-devops is id=2 |
products_list.json |
GET /supplierinvoices / /{id} |
200 | Supplier (fournisseur) invoices | Lines are included inline on the detail endpoint (same shape as /invoices/{id}). Sub-endpoint /lines returns HTTP 403 for ai_agent — not needed since inline lines work. |
supplierinvoices_list.json · supplierinvoice_detail.json |
GET /invoices/templates/{id} |
200 | Recurring invoice template detail | No list endpoint — probe ids 1..N. Empty ids return HTTP 200 with id=null (sentinel for "doesn't exist"). Fields: ref, socid, frequency, unit_frequency, nb_gen_done, date_when, date_last_gen, suspended, auto_validate, lines. |
invoice_template_km.json |
GET /documents/download |
200 | Download a stored document as base64 | modulepart=facture&original_file=<REF>/<REF>.pdf (URL-encode slashes). Returns {filename, content-type, filesize, content} with content base64. |
document_download_meta.json |
Not available on this account (intentionally): /setup/modules (admin-only), /invoices/templates (requires a path arg — different route shape; V2).
The dol-curl.sh wrapper
scripts/dol-curl.sh is a thin wrapper used by this skill and every sibling. It:
- Sources
../.env(relative to itself), so it works regardless of the caller's CWD. - Fails loudly if
.envis missing orDOLIBARR_URL/DOLIBARR_API_KEYis unset. - Prepends
DOLAPIKEYandAccept: application/jsonheaders + a 30s timeout. - Prints the response body to stdout.
- On HTTP ≥ 400, also prints
dol-curl.sh: HTTP <code> on <path>to stderr and exits 1 — soset -ein caller scripts surfaces the failure.
# Examples
./scripts/dol-curl.sh /users/info | jq .login
./scripts/dol-curl.sh '/invoices?limit=5&sortfield=t.datef&sortorder=DESC' | jq '.[].ref'
./scripts/dol-curl.sh /invoices/12 | jq '{ref, paye, total_ht, last_main_doc}'
./scripts/dol-curl.sh '/documents/download?modulepart=facture&original_file=FAC002-CL0001002%2FFAC002-CL0001002.pdf' \
| jq -r .content | base64 -d > /tmp/inv.pdf
Common gotchas
modeis an integer.?mode=1works;?mode=customer→ HTTP 400Invalid value specified for mode.- Empty list vs 404 vs 403.
/invoicesreturns[](200) on empty/permission-filtered./thirdpartiesreturns 404"No third parties found"./thirdparties/{id}returns 403 if the ACL hides the row. All three can mean "no permission" — the diagnostic is to hit/thirdparties/1directly (see Permission gotcha above). - Dates are usually unix epoch (seconds) on most objects:
.date,.datef,.date_lim_reglement,.tmsare integers.Exception:python3 -c "import datetime,sys; print(datetime.datetime.fromtimestamp(int(sys.argv[1])))" 1771887600 # → 2026-02-24 00:00:00/invoices/{id}/paymentsreturns.dateas the string"YYYY-MM-DD HH:MM:SS", not an int. Check the field type before parsing. payeis the paid flag,statusis the workflow state.status=2means "validated",paye=1means "fully paid". They're independent — a status-2 invoice can still be unpaid.last_main_docpaths are relative to Dolibarr's document root. For/documents/download, passmodulepart=facture+ URL-encodedoriginal_file=<dir>/<file>(encode the/).array_options=[]vs{}. Dolibarr returns[]when no extrafields are set and{}when there are some. Treat both as "no custom fields" in Python.
Pointers
- Workflow skill for invoice + thirdparty audits: dolibarr-invoice-audit.
- Workflow skill for payment-state and cash-receipt tracking: dolibarr-payments-state.
- Workflow skill for monthly TVA basis (CA3 / CA12 preparation): dolibarr-tva-reconciliation.
- Workflow skill for recurring invoice templates: dolibarr-recurring-templates.
- Workflow skill for point-in-time state archival: dolibarr-data-snapshot.
- Workflow skill for thirdparty completeness audit (any client / supplier): dolibarr-thirdparty-completeness.
- Workflow skill for supplier-side TVA déductible (CA3 lignes 19 / 20 / 17+24): dolibarr-tva-deductible.
- Workflow skill for composite CA3-ready TVA summary (collectée + déductible + net): dolibarr-tva-summary.
- Bank-side reconciliation (Qonto + Wise ↔ Dolibarr matching): arcodange-bank-reco.
- Email ingestion (Zoho Mail → supplier-invoice draft for Dolibarr): arcodange-email-ingest.
- Future workflow skills follow the
dolibarr-<topic>convention (ERP-internal) orarcodange-<topic>(cross-system). Each one depends on this skill for connection + permissions + endpoint reference; each one keeps its triggers focused on its specific business workflow.
Out of scope
- Writes. The
ai_agentkey is read-only by design. For corrections (edit a thirdparty, void an invoice, fix a mention), use the Dolibarr UI. - Admin endpoints.
/setup/*is gated to admins or to logins listed inAPI_LOGINS_ALLOWED_FOR_GET_MODULES; not available toai_agent. - Helm chart / Ansible / Kubernetes operations. Covered by
chart/,ansible/, and the project rootREADME.md.