Skip to content

A series by scenario

Inbound documents — the invoices somebody else sends to you — are delivered in real life by the tax authority. On the sandbox server we never talk to them: it is a server for trial runs, and nothing on it reaches a real institution.

The consequence is that your organisation there is empty. GET inbound returns a list with no entries, there is no document to fetch, and no document to decide on. So the entire inbound direction cannot be written or tried.

The endpoint described on this page fixes that: with one request it fills your organisation with a series of inbound invoices, one for every case your software has to survive.


This is the list of cases the series creates. Every scenario has a name, and those names are stable — they do not change between releases, so you can write them into your test.

If you read only one section of this page, read this one.

Scenario nameWhat state the document is inWhat your software gets when it sends a decision
deadlinePassedthe decision deadline has passed, so the tax authority has already accepted the document itself409 with code EARH-3006
alreadyAccepteda decision has already been made and confirmed — the document is accepted409 with the same code
alreadyRejectedthe same, but the document is rejected409 with the same code

⛔ These three states do not come about through a request. They come about through the PASSAGE OF TIME, or through an action that has already happened. That is why you cannot produce them through placing a single document either, which always creates a document in its initial state.

It is also why software that has not met them in a trial run meets them first in production — at a real customer, on a real invoice.

Scenario nameWhat the document carries
fullboth machine-readable content (UJP JSON) and a PDF
payloadOnlymachine-readable content only, no PDF
pdfOnlya PDF only
noContent⛔ neither

noContent is not an unfinished record. That is how a document looks when the privilege to fetch its content is missing at the tax authority: the document exists, appears in the list, and can be decided on — there is simply nothing to download.

📌 Which format returns content and which returns an error is described under Inbound documents → Downloading the content. That holds for every inbound document, not only for seeded ones.

Scenario nameWhat state the document is in
acknowledgedreceipt is already acknowledged, so the document is not returned when you ask for unfetched ones only; it carries an acknowledgement time
deadlineTodaythe decision deadline expires today — a boundary case, a decision still goes through
Scenario nameWhat amount it carries
zeroVatVAT is zero
creditNoteall amounts are negative
largeAmountan amount near the upper bound of the format
Scenario nameWhat it lets you try
secondSenderfiltering by the sender’s tax number — this document carries a different one from all the rest
olderThanWeekfiltering by arrival date, with by=created
updatedRecentlyfetching only what has changed, with by=updated

Filling with every scenario
curl -X POST https://sandbox.api.earhiva.mk/api/integration/v1/sandbox/seed \
-H "X-Api-Key: <YOUR KEY>" \
-H "X-Software-Id: <NAME AND VERSION OF YOUR APPLICATION>" \
-H "Content-Type: application/json" \
-d '{ "seed": 1 }'

The response lists the documents created. For each one it says which scenario it came from and what to expect of it — so your test can assert rather than guess:

Response · abridged
{
"seed": 1, // the series number that was used
"created": 15, // how many documents were created
"deleted": 0, // how many older seeded documents were removed; 0 without clearing
"documents": [
{ "scenario": "full", "docUid": "eah_i_…", "euid": "SEED-1-full",
"expect": "format=original and format=pdf return 200; format=ubl returns 404" },
{ "scenario": "deadlinePassed", "docUid": "eah_i_…", "euid": "SEED-1-deadlinePassed",
"expect": "POST decision returns 409 EARH-3006 — the deadline has passed" }
// …and one entry for every other scenario
]
}

docUid is the document’s identifier on our side. You then use it with GET inbound/{docUid}, GET inbound/{docUid}/file and POST inbound/{docUid}/decision.


The request body has three fields and none of them is required.

FieldTypeWhat happens if you omit it
seedinteger1 is used — not a random number
resettrue/falsenothing is deleted
scenarioslist of namesevery scenario in the catalogue is created

The same number gives the same series. Same amounts, same dates, same senders, same deadlines, in the same order.

That is the only reason the number exists. Without it every fill would produce a different series, so a test that failed yesterday would pass today — not because anything was fixed, but because the data is different.

What you sendWhat you get
"seed": 1series number 1
"seed": 1 againthe same series number 1
"seed": 2a different series — if you need a second, parallel one
the field omittedseries number 1
Clear what was seeded before, then fill again
curl -X POST https://sandbox.api.earhiva.mk/api/integration/v1/sandbox/seed \
-H "X-Api-Key: <YOUR KEY>" \
-H "X-Software-Id: <NAME AND VERSION OF YOUR APPLICATION>" \
-H "Content-Type: application/json" \
-d '{ "seed": 1, "reset": true }'

With reset: true the invoices this same endpoint created earlier are deleted — and nothing else.

⛔ A document that arrived by another route is left alone. That includes an invoice you placed through one invoice with your own content: it carries content you supplied and you may have spent a long time assembling it.

This lets your test start from the same state every time, which is what running it automatically requires.

When you are trying one case, you do not need the whole series:

Two invoices, nothing else in the organisation
curl -X POST https://sandbox.api.earhiva.mk/api/integration/v1/sandbox/seed \
-H "X-Api-Key: <YOUR KEY>" \
-H "X-Software-Id: <NAME AND VERSION OF YOUR APPLICATION>" \
-H "Content-Type: application/json" \
-d '{ "seed": 1, "reset": true, "scenarios": ["deadlinePassed", "alreadyAccepted"] }'

If you omit the field, every scenario is created.

⛔ A name that is not in the catalogue is refused, and then nothing is created. The response is 400 with code EARH-2030 and lists the allowed names. A typo will not produce half a series — that is a state you would not expect and would not notice.


The pattern we recommend for one cycle:

One cycle, repeatable as often as you need
1. POST sandbox/seed { "seed": 1, "reset": true }
→ keep the response; it is the expected result for step 3
2. GET inbound fetch them all
→ assert that the number of documents returned matches the `created` field
3. For every document in the response from step 1:
GET inbound/{docUid}
GET inbound/{docUid}/file?format=original → 200 or 404, per the scenario
POST inbound/{docUid}/decision → 200 or 409, per the scenario
4. Assert that no response is a 500

Step 1 returns everything step 3 needs.

💡 Do not write the scenario list into your test as a fixed value — read it from the response. Your test then stays correct when we add a new scenario, and adding one is not a change to the contract.


CodeWhen it appears
EARH-2030 · 400a name in scenarios that is not in the catalogue. Nothing is created; the response lists the allowed ones
EARH-1008 · 400the X-Software-Id header is missing
EARH-1009 · 404the endpoint was called on a server that is not the sandbox

It creates inbound invoices onlythe outbound direction is tried with a real POST outbound
It works only in your organisationno call sees into or changes anyone else’s
Nothing leaves for the tax authorityneither on creation nor when you send a decision
reset deletes only what was seededa document created another way stays
It does not accept content from youthat is one invoice with your own content