Any agent that can make HTTP requests can check obligations, quote and run a full simulated onboarding here, with no key and no account. A person still signs and pays.
By Razvan Toma · Founder · Last checked 1 November 2026
Yes, an AI agent can test this API on its own. It needs HTTP GET and POST and nothing else. A browsing-only agent can read these pages, but it can't run the steps.
| What the agent does | Endpoint | Effect |
|---|---|---|
| Check countries and likely streams | POST /check | Returns data only |
| Read the France questions | GET /france/questionnaire | Returns data only |
| Quote from live prices | POST /quote | Returns data only |
| Simulate each country's steps | POST /sandbox/onboardings | Simulated, creates nothing |
| Prepare a checkout link for a person | POST /onboarding-links | Returns a link, nothing is ordered until a person pays |
The base URL is https://epreurep.com/api/v1. It takes JSON over HTTPS, with a limit of 30 requests a minute per IP. Prices exclude VAT.
| File | Use it for |
|---|---|
| openapi.json | The exact contract of every endpoint |
| This page as Markdown | This quickstart as plain text |
| Full documentation as Markdown | Every rule, field and error |
| llms.txt | All pages and the facts to quote about EPR EU Rep |
These five steps are the whole journey. Each example shows what the live API returned.
curl -X POST https://epreurep.com/api/v1/check \
-H 'content-type: application/json' \
-d '{"countries":["FR","DE","ES"],"products":["cotton t-shirt","toy car"]}'{
"ok": true,
"countries": [
{ "code": "FR", "covered": true, "name": "France" },
{ "code": "DE", "covered": true, "name": "Germany" },
{ "code": "ES", "covered": true, "name": "Spain" }
],
"streams": ["household-packaging", "textiles-clothing-and-footwear-tlc", "toys"],
"indicative": true,
"note": "A first guess from product words. Whether you are the producer depends on your sales channels and establishment."
}streams is a first guess from the words in products, and indicative is always true. Confirm the streams with the client before you quote.
curl https://epreurep.com/api/v1/france/questionnaireIt returns the three required answers, every stream with its lowest plan, and the plan rules. Ask the client these questions rather than guessing.
curl -X POST https://epreurep.com/api/v1/quote \
-H 'content-type: application/json' \
-d '{"france":{"sells_to":"consumers","streams":["toys"],"tonnes":"up_to_1"},"germany":{"packaging_kg":{"paper":120,"plastic":40}},"spain":{"plan":"essential","nif":true}}'{
"ok": true,
"custom_quote": false,
"total": 977,
"currency": "EUR",
"lines": [
{ "country": "FR", "item": "France Standard", "eur": 380, "per": "year" },
{ "country": "DE", "item": "Germany packaging", "eur": 149, "per": "year" },
{ "country": "ES", "item": "Spain Essential", "eur": 199, "per": "year" },
{ "country": "ES", "item": "Spanish NIF", "eur": 249, "per": "one-off" }
],
"france": { "plan": "Standard", "streams": ["household-packaging", "toys"], "stream_count": 2, "tonnes": "up_to_1", "why": [] },
"germany": { "dual_system_estimate": { "eur": 121.43, "year": 2026, "vat_included": true, "charged": "in your client portal, before we accept the LUCID appointment" } },
"note": "Indicative. Scheme fees and eco-contributions are billed separately at cost. Prices exclude VAT."
}If custom_quote is true, the order is Enterprise: over 10 tonnes into France, or more than 3 French streams. total is null and only we can quote it.
curl -X POST https://epreurep.com/api/v1/sandbox/onboardings \
-H 'content-type: application/json' \
-d '{"countries":["DE"]}'{
"ok": true,
"sandbox": true,
"id": "sbx_...",
"countries": [{
"country": "DE",
"status": "order_paid",
"completed": ["order_paid"],
"next": { "step": "mandate_signed", "client_action_required": true },
"registration_number": null
}],
"complete": false
}One step moves on every 5 seconds. France has 3 steps, Germany 5 and Spain 4, so Germany finishes after 20 seconds.
ID=$(curl -s -X POST https://epreurep.com/api/v1/sandbox/onboardings \
-H 'content-type: application/json' -d '{"countries":["DE"]}' | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')
sleep 25
curl -s https://epreurep.com/api/v1/sandbox/onboardings/$ID{
"ok": true,
"sandbox": true,
"countries": [{
"country": "DE",
"status": "registered",
"completed": ["order_paid", "mandate_signed", "dual_system_paid", "appointed_in_lucid", "registered"],
"next": null,
"registration_number": "DE-TEST-0000000000000"
}],
"complete": true,
"note": "Simulated. No order, mandate, payment or registration exists. Test numbers are not valid anywhere."
}Stop polling when complete is true. The id holds the whole state, so it keeps working and nothing is stored on our side.
curl -X POST https://epreurep.com/api/v1/onboarding-links \
-H 'content-type: application/json' \
-d '{"partner":"northstar","france":{"sells_to":"consumers","streams":["toys"],"tonnes":"up_to_1"},"company":"Example Ltd","email":"owner@example.com"}'{
"ok": true,
"custom_quote": false,
"url": "https://epreurep.com/checkout/?fr=0&frs=toys&fr_seg=cons&ref=northstar#company=Example+Ltd&email=owner%40example.com",
"note": "The client confirms the details, signs and pays at this link. Paid orders are tracked to the partner."
}northstar is a demo code for testing. Partners use their own. Give the link to the person: company and email stay in the URL fragment, which never reaches a server.
DE-TEST-0000000000000 aren't valid anywhere.client_action_required is true, stop and tell the person what they have to do./check as a first guess. Who the producer is depends on sales channels and establishment.| Country | Steps for the client personally |
|---|---|
| France | Sign the mandate, which can be done online |
| Germany | Sign the mandate by hand or with a qualified electronic signature, and name the representative in LUCID |
| Spain | Sign the mandate. The power of attorney is then verified, which can take time |
Our team does the registrations and declarations, so they run in days, not seconds. More in why EPR registration can't be fully automated.
Errors return an HTTP status and a body with ok: false, an error code and a message.
| Status | error | What to do |
|---|---|---|
| 400 | invalid_json | Send a JSON object |
| 400 | invalid_request | message names the field. Fix it and retry |
| 400 | unknown_partner | Use a real partner code |
| 404 | not_found | Check the path or the sandbox id |
| 405 | method_not_allowed | Use POST on this path |
| 429 | Wait, then retry |
Paste this into any agent that can make HTTP requests.
Test the EPR EU Rep API from start to finish.
1. Read https://epreurep.com/developers/ai-agents/index.md and https://epreurep.com/developers/openapi.json
2. POST /api/v1/check for a seller of cotton t-shirts in FR, DE and ES.
3. GET /api/v1/france/questionnaire and POST /api/v1/quote with the answers.
4. POST /api/v1/sandbox/onboardings for FR, DE and ES, then poll the id until complete is true.
5. Report each step's status, and which steps need the client personally.
It is a simulation: never present a test number as a real registration.