Everything you need to take a client from first question to a paid order in France, Germany and Spain, and to test it in the sandbox first.
The API is JSON over HTTPS at https://epreurep.com/api/v1. It needs no key, and it allows any origin, so you can call it from a browser. An integration has four steps:
| Step | Call | What you get |
|---|---|---|
| 1. Ask the questions | GET /france/questionnaire | The French questions, streams and plan rules |
| 2. Price the answers | POST /quote | The plan, the yearly total and the German licence estimate |
| 3. Send the client to order | POST /onboarding-links | A checkout link tied to your partner code |
| 4. Test it | POST /sandbox/onboardings | A simulated run of each country's steps |
Every order is completed by the client at the checkout link, because the client has to accept the terms, pay and sign the mandate. Use the demo partner code northstar while you build. Paid orders count for a partner only under that partner's own code.
Limit: 30 requests a minute per IP. Prices are in euro and exclude VAT. Scheme fees and eco-contributions are billed separately at cost. All partner and API orders are billed by EPRFranceRep SAS, whichever countries they cover.
After the client pays, each country runs these steps in this order. They are the same steps the sandbox plays back.
| Country | Steps | The client does |
|---|---|---|
| France | order_paid, mandate_signed, registered | Signs the mandate |
| Germany | order_paid, mandate_signed, dual_system_paid, appointed_in_lucid, registered | Signs the mandate, names us in LUCID |
| Spain | order_paid, mandate_signed, power_of_attorney_verified, registered | Signs the mandate |
The client works in their own portal at portal.epreurep.com, which opens once they have paid. You follow their progress and your commission in the partner dashboard.
France needs three answers, the same three our checkout asks. All three are required. A request without one returns invalid_request and names the missing field.
| Field | Values | Question |
|---|---|---|
france.sells_to | consumers, mixed, businesses | Who do you sell to in France? |
france.streams | Stream keys, see below | Which product streams do you place on the French market? |
france.tonnes | up_to_1, 1_to_10, over_10 | How many tonnes of packaging a year? |
curl https://epreurep.com/api/v1/france/questionnaireYou never choose a plan. /quote works it out, and the checkout re-prices it the same way. The rules run from the top; the first match wins.
| Answers | Plan | Price a year |
|---|---|---|
| Over 10 tonnes, or more than 3 streams | Enterprise | We quote it |
sells_to is mixed or businesses, or professional packaging is listed | Plus Pro | €990, up to 3 streams |
| 1 to 10 tonnes, or any Plus stream (WEEE, batteries) | Plus | €990, up to 3 streams |
| Any Enterprise stream | Enterprise | Quoted by us first |
| Everything else | Standard | €190 per stream, up to 3 |
Two streams are added for the client, and both count towards the 3. Household packaging is added unless they sell only to businesses. Professional packaging is added when they sell to businesses.
| Key | Lowest plan |
|---|---|
household-packaging | Standard |
textiles-clothing-and-footwear-tlc | Standard |
electrical-and-electronic-equipment | Plus |
batteries | Plus |
furniture | Standard |
toys | Standard |
sports-and-leisure-goods | Standard |
diy-and-garden-tools | Standard |
professional-packaging | Plus Pro |
building-products-pmcb | Enterprise |
tyres | Enterprise |
chemical-products | Enterprise |
vehicles | Enterprise |
lubricants-and-mineral-oils | Enterprise |
medical-sharps | Enterprise |
medicines | Enterprise |
tobacco-products | Enterprise |
fishing-gear | Enterprise |
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"}}'{
"ok": true,
"custom_quote": false,
"total": 380,
"currency": "EUR",
"lines": [{ "country": "FR", "item": "France Standard", "eur": 380, "per": "year" }],
"france": {
"plan": "Standard",
"streams": ["household-packaging", "toys"],
"stream_count": 2,
"tonnes": "up_to_1",
"why": []
},
"note": "Indicative. Scheme fees and eco-contributions are billed separately at cost. Prices exclude VAT."
}why lists what raised the plan, so you can show it to the client. It is empty on Standard.
Over 10 tonnes or more than 3 streams cannot be ordered through the API. /quote returns custom_quote: true and no price:
{
"ok": true,
"custom_quote": true,
"total": null,
"currency": "EUR",
"lines": [],
"france": {
"plan": "Enterprise",
"streams": ["household-packaging", "toys"],
"stream_count": 2,
"tonnes": "over_10",
"why": ["Over 10 tonnes a year"]
},
"note": "Enterprise: more than 3 French streams or over 10 tonnes into France. We quote it first, so it cannot be ordered directly."
}/onboarding-links still returns a url, with custom_quote: true. It opens a quote request and has no payment step. We send a written quote, and the client pays through a link from us. If the order also includes Germany or Spain, the whole order waits for the quote.
Germany's licence is priced on this year's packaging in kilos, by material, at Noventiz, our dual system. Kilos are optional in the order: the client enters them in their portal. The licence is passed on at cost, and we accept the LUCID appointment only once it is paid.
| Field | Type | Meaning |
|---|---|---|
germany.packaging_kg | object | Kilos by material. Required unless already_licensed is true |
germany.already_licensed | boolean | Another dual system already licenses all of it |
germany.weee | boolean | Adds German WEEE, €500 a year |
germany.batteries | boolean | Adds German batteries, €500 a year |
| Material key | Material |
|---|---|
paper | Paper, board, carton |
plastic | Plastic |
glass | Glass |
ferrous | Ferrous metals |
alu | Aluminium |
bevcarton | Beverage cartons |
composite | Other composites |
other | Other materials |
curl -X POST https://epreurep.com/api/v1/quote \
-H 'content-type: application/json' \
-d '{"germany":{"packaging_kg":{"paper":120,"plastic":40}}}'{
"ok": true,
"custom_quote": false,
"total": 149,
"currency": "EUR",
"lines": [{ "country": "DE", "item": "Germany packaging", "eur": 149, "per": "year" }],
"note": "Indicative. Scheme fees and eco-contributions are billed separately at cost. Prices exclude VAT.",
"germany": {
"dual_system_estimate": { "eur": 121.43, "year": 2026, "vat_included": true, "charged": "in your client portal, before we accept the LUCID appointment" }
}
}dual_system_estimate is Noventiz's live price, VAT included, and it is separate from total. It is null if Noventiz gives no price at that moment; the quote itself still works.
Nothing is paid for the licence at checkout. Kilos you send with the order prefill the client's portal, where they can change them, see the live price and pay. They can sign the mandate and name us in LUCID first, but we accept the appointment only once the licence is paid.
With already_licensed: true, the client gives the licence details in the portal, and pays for any volume above what their other dual system covers.
| Field | Values | Meaning |
|---|---|---|
spain.plan | essential, standard | €199 or €249 a year. Defaults to essential |
spain.nif | boolean | Adds the Spanish tax number, €249 one-off, for clients who have none |
The NIF normally takes 5 to 6 days after the tax office appointment, and takes longer in busy periods. Foreign companies do not get a provisional NIF.
POST /onboarding-links takes the same body as /quote, plus your partner code. company and email are optional.
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."
}The link opens our checkout with the answers already filled in, and the client confirms them. Nothing is stored until the client starts. company and email sit in the URL fragment, so they never reach a server through the link.
The checkout prices the order again, so a changed link can't lower a price. Your code stays on the order, which is how it reaches your partner dashboard. Your partner code is public, so it is safe to use from a browser.
The sandbox plays back the steps from How an order runs. It creates no order, mandate, payment or registration, and it needs no partner code. The id holds the whole state, so nothing is stored.
curl -X POST https://epreurep.com/api/v1/sandbox/onboardings \
-H 'content-type: application/json' \
-d '{"countries":["DE"]}'One step moves on every 5 seconds. Read the status with GET /sandbox/onboardings/{id}:
{
"ok": true,
"sandbox": true,
"id": "sbx_...",
"countries": [{
"country": "DE",
"status": "mandate_signed",
"completed": ["order_paid", "mandate_signed"],
"next": { "step": "dual_system_paid", "client_action_required": false },
"registration_number": null
}],
"complete": false,
"note": "Simulated. No order, mandate, payment or registration exists. Test numbers are not valid anywhere."
}client_action_required is true for a step the client has to do personally, such as signing the mandate. When complete is true, each country has a test registration_number, for example DE-TEST-0000000000000. Test numbers are not valid anywhere.
The checks, quotes and links are live even while you test: /quote returns real prices and /onboarding-links returns a real checkout. Use northstar so that no test counts as a partner's lead. You can also run all of this from the sandbox on the developers page.
Apply on the partner page. Once approved, you get your own code, your dashboard, and 20% commission on what your clients pay us, renewals included. northstar is a demo code that earns nothing.
Errors return an HTTP status and a body with ok: false, an error code and a message you can show.
{ "ok": false, "error": "invalid_request", "message": "france.tonnes is required: up_to_1, 1_to_10 or over_10." }| Status | error | Meaning |
|---|---|---|
| 400 | invalid_json | The body isn't a JSON object |
| 400 | invalid_request | A field is missing or has a value we don't accept. message names it |
| 400 | unknown_partner | The partner code doesn't exist |
| 404 | not_found | Unknown path or sandbox id |
| 405 | method_not_allowed | Use POST on this path |
| 429 | Over the limit of 30 requests a minute. Wait and try again |
Base URL https://epreurep.com/api/v1. OpenAPI file
POST /api/v1/checkWhich of your countries we cover, and a first guess at the product streams. The streams can go straight into /quote.
curl -X POST https://epreurep.com/api/v1/check \
-H 'content-type: application/json' \
-d '{"countries":["FR","DE"],"products":["cotton t-shirt"]}'GET /api/v1/france/questionnaireThe questions our French checkout asks, with every stream, the lowest plan for each and the plan rules. Build your form from it.
curl https://epreurep.com/api/v1/france/questionnairePOST /api/v1/quoteThe yearly price, calculated by the same code as our checkout. France needs the three answers from the questionnaire and returns the plan they lead to. Germany needs this year's kilos and returns the licence estimate. custom_quote is true for Enterprise, which we quote first.
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}}'POST /api/v1/onboarding-linksA checkout link tied to your partner code, with the answers already in it and company and email filled in. Company and email stay in the URL fragment. Takes the same body as /quote. For Enterprise, the link opens the quote request and cannot be paid. northstar is a demo code for testing: use your own once you are a partner.
curl -X POST https://epreurep.com/api/v1/onboarding-links \
-H 'content-type: application/json' \
-d '{"partner":"northstar","germany":{"packaging_kg":{"paper":120,"plastic":40}},"company":"Example Ltd","email":"owner@example.com"}'POST /api/v1/sandbox/onboardingsStarts a simulated onboarding for FR, DE or ES and returns an id. One step moves on every 5 seconds.
curl -X POST https://epreurep.com/api/v1/sandbox/onboardings \
-H 'content-type: application/json' \
-d '{"countries":["DE"]}'GET /api/v1/sandbox/onboardings/{id}The simulated status per country. client_action_required marks the steps a client has to do personally.
curl https://epreurep.com/api/v1/sandbox/onboardings/sbx_...