You wire up your integration with the Fatoora platform, test sending an e-invoice, and it bounces back rejected with a specific code: 1015. This code tells you that the Accept-Version header in your request is missing or its value is invalid, so the gateway rejects the request before it ever looks at the invoice payload. This page is a dedicated guide to error code 1015 alone, within the Qoyod e-invoicing system.
What sets this code apart from other invoice errors is that it has nothing to do with the invoice content. The invoice may be perfectly valid, yet the request is rejected at the version level because you never told the platform which version of the interface you are addressing. The correct value for this header is V2. Any other value, or the header being absent entirely, produces code 1015. So fixing this code does not touch the invoice data at all; it touches the request-building layer in your code.
The target audience here is the developer or technical integration owner building the connection between an accounting system and the Fatoora platform. This code belongs to the family of gateway errors that precede invoice validation, within the guide to ZATCA e-invoicing errors. And because its root lies in the headers sent with the request, its closest reference is the Fatoora platform API endpoints page, which lists the common headers for each endpoint. English technical tokens such as Accept-Version andV2 are kept as-is because they are the actual field names in the API.
Accept-Version: V2 (mandatory)
Authorization: Basic (CSID)
Content-Type: application/json
A missing Accept-Version produces code 1015
What error code 1015 means exactly
Code 1015 is a gateway-level rejection decision, not a warning. The Fatoora platform issues it when a request arrives without the Accept-Versionheader, or with a header whose value does not match a version the platform supports. This header’s job is to tell the interface which version of the technical contract you are dealing with, so the platform knows how to interpret your request and which response shape to return to you.
The platform evolves its interfaces over time, and a new version appears without immediately retiring the old one. So the platform does not assume a default version on your behalf; it asks you to pin it explicitly in every request. This design protects your integration: as long as you pin the version, the interface behavior does not shift under your feet when the platform upgrades. But the price is that omitting the header means a request with no version, so the gateway rejects it with code 1015.
The only correct value accepted today is V2, the interface version that serves phase two of e-invoicing. Note that the value is a capital letter then a digit, in the exact form V2 specifically. Any other spelling such as v2 in lowercase, or 2 without the letter, or 2.0, or version 2, may not match the value the gateway expects, so it issues code 1015 even though you «sent a version».
The practical takeaway is that code 1015 gathers under its umbrella two closely related problems: the header is missing entirely, or the header is present but its value or name is incorrect. Both trace back to the request-building layer in your system, not to the invoice. So fixing code 1015 always starts with one question: which headers is my code actually sending with each request?
Before we go into detail, one core point deserves clarifying. Many new integration developers pour all their attention into the invoice payload and the validity of its fields, forgetting that the gateway inspects the headers first, before it looks at the payload at all. This explains why code 1015 is confusing: the developer sees a rejection response and assumes it is an invoice problem, so they spend their time reviewing tax fields to no avail, while the root is a missing header line at the top of the request.
Case one: the Accept-Version header is missing entirely
This is the clearest case of code 1015. It happens when your code builds the request and adds the authentication header andContent-Type, but omits Accept-Version entirely. So the platform receives a request carrying no version, and rejects it at the gateway before reading the invoice. The response message comes in this form:
The root cause. The request-building layer in your system does not add the header. The most common form of this is that the developer assembled the headers object by hand and included authentication and content type but forgot the version. Another form is that the system relies on a default setting of an HTTP library that does not add this header, or that a helper function building the headers was reused from another context that does not need to pin the version.
The fix. Add the Accept-Version: V2 header explicitly to every request that addresses the platform endpoints, whether compliance, authentication, or reporting. Do not assume the gateway will pick a default version, because it does not. Make the header part of the shared function that builds every request, rather than adding it by hand at each call site individually.
For a practical check, print the full headers object right before sending, or capture the actual request with a proxy tool that shows you the raw HTTP header. Do not rely on what you think the headers are, but on what actually leaves your system. If you do not find the Accept-Version line in the captured header, you are in this case, and the fix is to add it in the shared building layer.
This example shows the correct shape of the request header after adding it:
Case two: the Accept-Version value is incorrect
The second face of code 1015 is that the header is present but its value does not match what the platform accepts. The most common form is a case mismatch: sending v2 in lowercase instead of V2 in uppercase. Other forms are sending the digit alone 2, or a decimal form 2.0, or a descriptive text such as version 2, or an old version no longer supported such as V1.
The root cause. The value goes wrong for several reasons. Your code may generate it dynamically from a variable holding a different format, or read it from a config file where the version was written in a wrong form, or a transform function is applied to it that changes the letter case, turning V2 into v2. Likewise, spaces or hidden characters may creep in around the value when it is built as text, so it does not match the expected value character for character.
The fix. Pin the value to V2 , a single uppercase letter followed by the digit, with no spaces, prefix, or suffix. Avoid any downstream transform function that touches letter case. Store the value in one clear setting, and read it as-is without processing. If you build the value as text, verify it before sending with a strict comparison that respects letter case, not one that ignores it.
Pay attention to a subtle point about the header name itself, not its value. HTTP header names are case-insensitive by standard, so sending accept-version or Accept-version is acceptable name-wise. But the value V2 is case-sensitive because the platform matches it as text. Do not mix the two rules: the header name is flexible, but its value is strict.
| Accepted | Rejected (1015) |
|---|---|
| V2 | v2 (lowercase) |
| V2 | 2 or 2.0 |
| V2 | version 2 or V1 |
Where to set the Accept-Version header in the request layer
Now that you know the correct value, the most important question remains: where do you put it in your code so you never forget it in any request? The answer determines whether code 1015 disappears for good or returns at an endpoint you forgot.
The right place is the shared HTTP client layer. Make the Accept-Version: V2 header part of the default headers of the HTTP client that addresses the platform, not part of each individual call. This way every request inherits the header automatically, leaving no room for human forgetfulness. Most HTTP libraries let you define default headers at the client or session level, so take advantage of that.
Make the version value a configuration variable, not a value buried in the code. Put V2 in the config file or in environment variables, and read it from there. This makes it easy to update the version one day without chasing its value across dozens of spots in the code. But be sure the setting is a fixed string not subject to transformation, so a wrong letter case does not leak in.
Unify header building in a single function. Write a single function that builds the shared headers object for all platform requests: authentication, content type, error-message language, and the version. Then call this function from every point. This ensures any new request you add in the future carries all four headers without you thinking about it.
Remember that Accept-Version is a header shared across all endpoints, not specific to one endpoint over another. Compliance requests, cryptographic stamp identifier (CSID) issuance, clearance, and reporting all need it. So its natural place is the shared layer that all these requests pass through, not the logic of a particular endpoint.
A practical point for organizations that run both a test and a production environment at the same time. Many teams separate the base URL and the cryptographic stamp identifier between the two environments, but build the headers in two separate places, so they forget the version in one of them. The result is that the integration works in test and issues code 1015 in production, or vice versa. The fix is for both environments to share the same header-building function, leaving only the URL and the identifier as the variable between them, not the header logic. This makes it impossible for one environment to carry the version while the other forgets it.
Also watch out for middleware libraries or internal API gateways your request may pass through before it leaves. Some of these layers rewrite the headers or drop what they do not recognize, dropping Accept-Version without your knowledge even though you added it in your code. If you capture the header inside your application and see it intact but code 1015 persists, capture the request at the last point before it leaves your network, to expose any middleware dropping the header along the way.
Let Qoyod handle the request headers on your behalf
From pinning the interface version in every request to building the phase-two-compliant XML payload, Qoyod manages the entire connection layer to the Fatoora platform, so code 1015 never reaches you in the first place.
Start your free trial and send your invoices with valid requests
How to read and diagnose the 1015 message step by step
When you get code 1015, follow a fixed diagnostic sequence instead of guessing. The goal is to determine which of the two cases you are in before you write any fix, and to confirm the problem is in the header, not the invoice.
First, read the category field in the response. A value of MISSING_HEADER means the header is missing entirely, and a value of INVALID_HEADER_VALUE means it is present but its value is rejected. This field alone splits the problem in two and saves you half your diagnosis time.
Second, capture the actual request header that left your system. Use a proxy tool or a log that prints the headers verbatim before sending. Look for the Accept-Versionline. If you do not find it at all, you are in case one. If you find it, move on to inspecting its value.
Third, compare the captured value against V2 character by character. Watch for letter case and hidden spaces around the value. The value v2 in lowercase, or 2 alone, or the presence of an extra space, all put you in case two. Paste the value into an editor that shows invisible characters to make sure it is V2 clean.
Fourth, fix the request layer, not the single request. Code 1015 recurs on all your requests as long as the header-building layer is one and the same. Fixing a single request does not solve the root. Modify the shared layer, retest it in the sandbox environment, then resend the affected requests.
Read category
Inspect the actual request headers
Compare the value against V2 character by character
Also distinguish code 1015 from the other gateway errors that precede invoice validation. An authentication error is issued when the access token is missing or expired, not when the version header is missing. And a content-type error is issued when Content-Type is missing or its value is not application/json. If you inspect the category field and find it points specifically to the version header, you are facing code 1015, not an authentication or content-type problem. This distinction stops you from wasting time renewing the access token while the flaw is in the version line.
It helps to log, for each request, the headers that actually left along with the timestamp and response status. This log turns diagnosing code 1015 from guessing into a direct review: you open the log, search for the rejected request, and see which headers it actually carried. The log itself reveals whether the problem is in all requests or in a particular request type.
Finally, beware of the «fix» that looks quick and does not address the root: adding the header by hand in the failed request alone and then resending it. This passes the single request but leaves the rest of the requests issuing code 1015. Address the shared building layer once instead of putting out the fire request by request.
How Qoyod helps you avoid code 1015
The core idea is that most cases of code 1015 arise from a request layer the integration team writes itself, forgetting a header or getting its value wrong. When an integrated accounting system takes over building the request on your behalf, this class of errors disappears because it leaves the user no chance to get the headers wrong in the first place.
Qoyod builds every request addressing the Fatoora platform with its complete and correct headers, including Accept-Version with its correct value V2, in a unified connection layer that all endpoints pass through. And because the platform is directly linked to the Fatoora platform, it builds the phase-two-compliant XML payload and sends it with the correct technical contract without manual intervention. Qoyod also manages the cryptographic stamp identifier (CSID) for each organization automatically within the e-invoicing system, handling the technical side instead of it being a burden on your team.
When the platform upgrades its version one day, the integrated system updates the version value in its shared layer, so you do not need to track every call site in your code. The result is that the accountant focuses on the validity of the tax data, while the platform handles the technical request details that produce code 1015.
This difference shows clearly in organizations that connect more than one system or branch to the Fatoora platform. The more connection points there are, the higher the chance that one of them forgets the version header or gets its value wrong. When a system tested on millions of requests takes over building the headers, code 1015 becomes a rare event instead of an obstacle the integration team chases at every new connection point.
Learn about the Qoyod e-invoicing system system and its phase-two requirements, to understand where the request-building layer sits within the compliant invoice-issuance cycle. And for the technical detail of setting up the platform connection, see the authentication in the invoicing interface guide, which explains the header accompanying every request.
When error 1015 appears in test and when in production
The severity of code 1015 differs by the environment it appears in. In the sandbox environment, its appearance is useful because it exposes a request-layer flaw early, before it touches real invoices. Treat any 1015 in test as an alarm bell worth fixing the shared layer for immediately, not bypassing it by adding the header by hand in a single request.
In production, however, code 1015 is more dangerous because it means invoices that do not actually reach the authority, so reporting is delayed. More dangerous still is that the code may appear suddenly after a platform upgrade if the version value was buried in the code and not updated. Here is where the value of making the version a configuration variable that is easy to update shows, not a fixed value scattered through the code.
The practical rule: handle code 1015 in test by fixing the header layer, and in production by stopping the affected requests immediately, fixing the shared layer, then resending with valid headers. Do not mix the two fixes.
A practical prevention checklist for error 1015
Prevention is cheaper than the cure. Review the following points in your code before you launch your integration into production, as they cover the two root causes of code 1015.
The version header in the shared layer. Make sure Accept-Version is defined within the default headers of the HTTP client, not in each individual call. Every request inherits it automatically, so no room for forgetting remains.
The value V2 character by character. Pin the value to V2 Uppercase letter and digit, with no spaces and no downstream letter-case transform. Verify it with a strict comparison that respects letter case.
The version in the config, not the code. Put the version value in the config file or environment variables, and read it as-is. This makes it easy to update in the future without chasing it through the code.
A single function to build the headers. Unify building the shared headers in a single function that every request calls. This way any new request carries all four headers without thought.
Capturing the request header in test. Make your test capture the actual HTTP header and confirm the presence of Accept-Version: V2 with its correct value. Stop the deployment if the header is missing or its value is corrupt.
Reviewing the headers at every new connection point. When connecting a new system or branch to the platform, make sure it passes through the same shared layer, rather than building its requests with its own headers that may miss the version.
From fixing the single request to fixing the connection layer
The biggest mistake in dealing with code 1015 is handling it request by request. Because its root is in the header-building layer, the single request you fixed by hand does not stop the code from returning in the next request as long as the layer has not changed. Treat every appearance of code 1015 as a sign of a flaw in the connection layer, not in the individual request.
The root fix is twofold: put Accept-Version: V2 in the default headers of the shared layer, and read its value from a fixed setting without transformation. These two layers together close the two doors code 1015 enters through: the missing header, and the corrupt value.
Once you have pinned and tested this layer, code 1015 turns from a recurring error chasing you into a rare case you catch before it reaches the platform. More importantly, your fix holds up across multiple connection points and across platform upgrades, because it addresses the connection layer, not the symptoms. If you want to avoid writing this layer from scratch, adopting an accounting system that takes over building the request and its headers puts these settings in place by default, so you start from a point free of code 1015.
Frequently asked questions
What is the correct value for the Accept-Version header?
The correct value accepted today is V2, an uppercase letter followed by the digit, and it is the interface version that serves phase two of e-invoicing. Any other spelling such as v2 in lowercase, or 2 alone may issue code 1015.
Is the Accept-Version header name case-sensitive?
The header name is case-insensitive by the HTTP standard, so accept-version andAccept-Version are equivalent name-wise. But its value V2 is case-sensitive because the platform matches it as text, so be sure to use the uppercase letter.
Is code 1015 a problem in the invoice or in the request?
Code 1015 is a problem in the request, not the invoice. The gateway inspects the headers before it looks at the invoice payload, so it rejects the request at the version level even if the invoice is perfectly valid. The root is in the request-building layer in your code.
At which endpoints do I need to send Accept-Version?
At every endpoint without exception: compliance, cryptographic stamp identifier issuance, clearance, and reporting. The header is shared across all interfaces, so its natural place is the shared connection layer that all requests pass through.
Why did code 1015 appear suddenly even though my integration was working?
Most likely the version value was buried in the code and not updated after a change in the platform, or a reused function dropped the header. Make the version an easy-to-update configuration variable, and unify building the headers in a single function, so the code does not surprise you after any change.
How do I avoid code 1015 without building the request layer myself?
Use an integrated accounting system that builds the request and its headers on your behalf. Qoyod places the Accept-Version: V2 header in a unified connection layer for all endpoints, so code 1015 never reaches you in the first place.