Partner API

Partner overview

A partner is an OAuth integrator who also brings its own clients to Tax Star E-Invoice. Pre-register a client by its TIN; once its company is on the platform you are linked to it as an editor, and you follow its activity as counts. Everything is done from the app you already use, and a webhook can tell you the moment a client is onboarded.

At a glance

What it is
Bring your own clients to Tax Star. Pre-register each one by its TIN; when its company is on the platform you are linked to it and added as an editor, so your application can manage it.
Who it is for
Any approved OAuth app with the full API. There is nothing to apply for.
What you get
Editor access to every company linked to you, and its sent, received and failed counts.

In three lines

  1. POST /oauth/partners/pre-register with the client's tin and email.
  2. Read its status with GET /oauth/partners/pre-register/clients: pending or entity_created.
  3. Optionally subscribe to the client.onboarded webhook to be told the moment it happens.

Who this is for

WhoWhat it means
A partnerAn OAuth integrator: any approved app with the full OAuth API, owned by an ordinary account. There is nothing to apply for. If your app can call /oauth/entities, it can call /oauth/partners/*.
Not a partnerAn app owned by a reseller account. It can call the reseller endpoints only, and answers 403 insufficient_scope here. See the Reseller API.

Resellers use a parallel flow with the same rules, on the Reseller API page. A person who is both has one list in each, and neither sees the other's.

What a partner does

WhatHow
Pre-register a clientGive the client's TIN and email, with an optional company name and notes.
Be linked or toldCompany not registered yet: your request waits, and you are told when it is. Registered with nothing attached: linked to you at once.
Manage the company as an editorOn a link your account joins the company's members as an editor. Your applications can then read it, connect an ERP, validate and send invoices, and manage members other than admins. The owner is emailed and can remove you.
Follow its activityThree counts per linked client: invoices sent, received and failed, in the client list and through the webhooks.

You see only the clients you pre-registered yourself, never another partner's or reseller's. The endpoints are on Partner clients in the API reference.

Quick start: pre-register your first client

Four steps. The fourth is optional.

  1. Step 1: Get an access token

    Exchange your app's client_id and client_secret. Create the app on Create an app if you have none.

    POST /oauth/token
    curl -X POST https://api.taxstar.app/einv/oauth/token   -H "Content-Type: application/json"   -d '{
        "grant_type": "client_credentials",
        "client_id": "tsk_...",
        "client_secret": "tsec_..."
      }'
    # Use the access_token it returns as: Authorization: Bearer <access_token>
  2. Step 2: Pre-register a client

    Send its 10-digit tin and email. A 201 means it is stored, and linked_existing_entity says whether it was linked at once.

    POST /oauth/partners/pre-register
    curl -X POST https://api.taxstar.app/einv/oauth/partners/pre-register \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "tin": "1000000000",
        "email": "client@example.com",
        "company_name": "Client LLC"
      }'
  3. Step 3: Read its status

    pending until a company with that TIN is registered, then entity_created with its counts.

    GET /oauth/partners/pre-register/clients
    curl https://api.taxstar.app/einv/oauth/partners/pre-register/clients?status=pending   -H "Authorization: Bearer $ACCESS_TOKEN"
  4. Step 4: Optional: get told when it happens

    Subscribe to client.onboarded instead of polling. Skip this step and the status above still works.

    POST /oauth/webhooks
    curl -X POST https://api.taxstar.app/einv/oauth/webhooks \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://example.com/taxstar/webhooks",
        "events": ["client.onboarded"]
      }'

What pre-registering does

Pre-registering tells Tax Star which company you are bringing: its TIN, and the email of the person to reach. Do it with POST /oauth/partners/pre-register, before or after the company is on the platform. You then see when each client is onboarded and follow its activity as counts.

The partner and reseller flows are the same, with separate lists: what you pre-register here is never shown to the other, even when one person holds both.

Partner or reseller: what each gets

One flow, two kinds of access. The difference is what a link gives you, and it is the same table on both pages.

What a partner and a reseller each get when a client is linked
 Partner (this page)Reseller
Who it is
Any approved OAuth app with the full API
A reseller account, or an account given reseller access
Pre-register endpoint
POST /oauth/partners/pre-register
POST /oauth/reseller/pre-register
Added to the linked company's members
Yes: As an editor, at once
No: Never. It holds no role on the company
Can use the company (connect an ERP, validate and send invoices)
Yes: Yes, as an editor
No: No. Counts only
Sees sent, received and failed counts
Yes: Yes
Yes: Yes
Company's owner is emailed
Yes: Yes, with the partner email
Yes: Yes, with the reseller email
client.onboarded webhook
Yes: Subscribe at /oauth/webhooks or in the app settings
Yes: Subscribe at /oauth/reseller/webhooks
Its own list of clients
Yes: Yes, separate from a reseller's
Yes: Yes, separate from a partner's
The client's consent
Yours to obtain. Tax Star does not check it
Yours to obtain. Tax Star does not check it

See what happens to a request

From your request to the link and what follows it. A request gets exactly one of the outcomes below.

You

Pre-register the client

POST /oauth/partners/pre-register with the client's tin and email.

then

Tax Star

Checks the request

First the cheap refusals: bad TIN or email 400, suspended account 403, already requested or reserved by another partner/reseller 409. Then it looks for a company with this TIN.

one of four outcomes

201 · pending

No company has this TIN yet

Your request waits.

When a company registers with this TIN it becomes entity_created and the link below follows.

201 · linked now

Registered, and nothing is attached

No reseller, partner or OAuth app is attached to the company.

You are linked at once: entity_created, linked_existing_entity: true. First to ask wins.

409 · refused

Registered, and something is attached

A reseller, partner or OAuth app already is.

Nothing is stored. The answer never says who.

409 · tin_reserved

Another partner or reseller asked first

One TIN can be linked to only one partner or reseller across Tax Star.

Your request is not stored.

whenever a request is linked: now, or later when the company registers

Tax Star

Then, on every link

  • The company's owner is emailed once, and can contact support if they did not allow it.
  • client.onboarded is sent to your webhook, if you have one.
  • Each step is recorded with the TIN, how, when and why.
and the access differs

Partnerthis page

Added as an editor

Your account joins the company's members as an editor: connect an ERP, validate and send invoices, manage non-admin members. An existing role is kept; an owner is not added; the owner can remove you.

Reseller

Stays limited

Never added to the company's members and holds no role on it. You see only the counts: invoices sent, received and failed.

Read top to bottom. The outcomes are exclusive: a request gets exactly one.

A client's status is one of two words, and a refused request is never stored.

  1. pendingno company has the TIN yet; activity is null
  2. then, a company registers with the TIN, or was already registered and free
  3. then, one of these outcomesentity_createdlinked; counts appear in activitynot_linkedlegacy duplicate; another request won

refused (409 / 400 / 403)new request not stored.

Newly refused requests are not stored. A legacy duplicate that lost to an earlier reservation appears as not_linked.

Follow one client from start to finish

Seven steps, and who does each: you, Tax Star, or the company's owner.

  1. YouPre-register the client

    Send the client's TIN (10 digits) and email, with an optional company name and notes.

  2. Tax StarChecks the TIN

    No company yet: your request waits (pending). A company already registered with nothing attached: linked at once. Something attached: refused with 409.

  3. Tax StarLinks the company

    Now, or later when a company registers with that TIN. The request becomes entity_created, and the entity is marked as yours.

  4. Company ownerIs told by email

    Tax Star emails the owner (the partner email), names you, and asks them to contact support if they did not allow it.

  5. Tax StarAdds you as an editor

    Your account joins the company's members as an editor. An existing role is kept, an owner is not added, and the owner can remove you.

  6. Tax StarSends client.onboarded

    To your webhook, if you subscribed. Up to about a minute later, retried up to 8 times.

  7. YouManage the company and follow its counts

    Use the company through your application as an editor, and read sent, received and failed counts from the client list.

Work out the client's TIN

Keep the first ten digits of the company's TRN. That is the TIN, and it is the only identifier this API takes.

TermMeaning
TRNThe company's 15-digit Tax Registration Number, for example 100000000000003.
TINThe first ten digits of the TRN: 1000000000 for the TRN above. It starts with 1.
What this API takesOnly the TIN, in a field called tin. A 15-digit value in tin, or a field called trn, is refused with 400 and nothing is stored. Take the TIN from a TRN by keeping its first ten digits.
What it returnstin in every response, list and webhook. The 15-digit TRN is never returned, so you never have to match two spellings of the same company.

Know what each answer means

Each request has exactly one outcome. The 201 outcomes store a client. A 409 tin_reserved means another partner or reseller requested the TIN first.

SituationResponselinked_existing_entityclient.statusWebhooks
No company has this TIN yet201falsependingNone now. client.onboarded later, when a company with this TIN is registered.
A company is registered and nothing is attached to it: no reseller, no partner and no OAuth application201trueentity_createdclient.onboarded, now.
Another partner or reseller already reserved this TIN409 tin_reservedno client is storednoneNone.
A company is registered and a reseller, a partner or an OAuth application is already attached409 entity_existsno client is storednoneNone.
You already requested this TIN409no client is storednoneNone.
The tin is not 10 digits starting with 1, or the email is not valid400 invalid_requestno client is storednoneNone.
The account is suspended403 account_suspendedno client is storednoneNone.

Nothing attached means no reseller, no partner and no OAuth application is connected to the company. If the company is already registered, the first requester gets it: the link is made in a single step, so a second request is refused. A refusal never says who holds the company.

What a link gives. A partner is added to the company's members as an editor, at once, in both linked cases. A role you already hold is left as it is, and a partner that owns the company is not added.

Copy-paste examples

The request is the same for every outcome. Replace ACCESS_TOKEN with a token from POST /oauth/token; the values below are fake.

POST /oauth/partners/pre-register
curl -X POST https://api.taxstar.app/einv/oauth/partners/pre-register \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tin": "1000000000",
    "email": "client@example.com",
    "company_name": "Client LLC"
  }'

201: no company yet. The request waits. activity is null until a company exists.

201 — pending
{
  "message": "Client requested",
  "linked_existing_entity": false,
  "client": {
    "id": "6710c0ffee0000000000abcd",
    "tin": "1000000000",
    "email": "client@example.com",
    "company_name": "Client LLC",
    "notes": "",
    "status": "pending",
    "created_at": "2026-10-01T09:00:00+00:00",
    "entity_created_at": null,
    "activity": null
  }
}

201: already registered, nothing attached. Linked at once. The message reads “This company is already registered and nothing else was attached to it, so it has been linked to you”.

201 — linked to an existing company
{
  "message": "This company is already registered and nothing else was attached to it, so it has been linked to you",
  "linked_existing_entity": true,
  "client": {
    "id": "6710c0ffee0000000000abce",
    "tin": "1000000000",
    "email": "client@example.com",
    "company_name": "Client LLC",
    "notes": "",
    "status": "entity_created",
    "created_at": "2026-10-01T09:00:00+00:00",
    "entity_created_at": "2026-10-01T09:00:00+00:00",
    "entity_id": "6710c0ffee0000000000abcf",
    "activity": { "sent": 0, "received": 0, "failed": 0 }
  }
}

409: something is already attached. Nothing is stored.

409 — entity_exists
{
  "error": "A company with this TIN is already registered and is linked to a reseller or an application",
  "code": "entity_exists"
}

409: you already requested it.

409 — already requested
{
  "error": "You have already requested a client with this TIN"
}

409: another partner or reseller reserved the TIN. Nothing is stored for this request.

409 — tin_reserved
{
  "error": "A client with this TIN has already been requested",
  "code": "tin_reserved"
}

400: not a TIN. A 15-digit TRN gets this too.

400 — invalid_request
{
  "error": "invalid_request",
  "details": [
    {
      "type": "value_error",
      "loc": ["tin"],
      "msg": "Value error, TIN must be 10 digits, starting with 1"
    }
  ]
}

Get told when a client is onboarded

A client is onboarded when a company with its TIN is registered on Tax Star. A client.onboarded webhook tells you the moment it happens. It is optional, and the only client event: a request that is still pending sends nothing.

  1. Tax Star

    A client is linked

    A pre-registration links to a company, now or when it registers.

    then
  2. Tax Star

    The event is recorded

    client.onboarded is queued for your webhooks.

    then
  3. Tax Star

    Delivered within about a minute

    A scheduled job sends it, signed with X-Taxstar-Signature.

    then
  4. Your server

    Receives the POST

    Verify the signature and answer any 2xx within 10 seconds.

the answer decides

2xx

Done

The delivery is recorded as successful. Nothing more happens.

anything else

Retried, then given up

Tax Star retries with growing waits and gives up after 8 attempts. Missed events are not replayed: list your clients to catch up.

Delivered to your webhook (subscribed at /oauth/webhooks or in the app settings). Events recorded before you created a webhook are not sent to it.
Step
1. SubscribeCreate a webhook for client.onboarded: in the Webhooks panel of your app's settings, where the event is in the list, or with the API. The response shows the signing_secret once: store it.
2. Pre-register as usualNothing changes in the request. The webhook is optional: without one you still get the status by listing or getting your clients.
3. Tax Star sends the eventWhen a company with that TIN is registered, or at once when a request is linked to a company that already exists, Tax Star POSTs a signed JSON body to your URL. It can arrive up to about a minute after the event.
4. Verify and acknowledgeCheck the X-Taxstar-Signature header with your signing_secret, do your work, and answer with any 2xx within 10 seconds. A failure is retried with backoff and given up after 8 attempts.
5. Catch up if you missed onePast events are not replayed, and an event recorded before you created the webhook is not sent to it. List your clients (status entity_created) to find what you missed.

Subscribe. In the Webhooks panel of your app's settings, tick client.onboarded in the list, or call POST /oauth/webhooks. * includes it.

POST /oauth/webhooks
curl -X POST https://api.taxstar.app/einv/oauth/webhooks \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/taxstar/webhooks",
    "events": ["client.onboarded"]
  }'
201 — webhook created
{
  "message": "Webhook created. Store the signing_secret now - it is used to verify event signatures and is shown in full only on create and rotate.",
  "webhook": {
    "id": "6710c0ffee0000000000dcba",
    "url": "https://example.com/taxstar/webhooks",
    "events": ["client.onboarded"],
    "is_active": true,
    "signing_secret": "whsec_..."
  }
}

Try it. Send a signed test event to your URL before you rely on it.

POST /oauth/webhooks/{webhook_id}/test
curl -X POST https://api.taxstar.app/einv/oauth/webhooks/WEBHOOK_ID/test \
  -H "Authorization: Bearer $ACCESS_TOKEN"

What you receive. The same body for a pending request that onboards later and for a request linked at once. It carries the client's id, TIN, company name, status and timestamps, never its email or the company's id.

client.onboarded
{
  "event": "client.onboarded",
  "created_at": "2026-10-03T12:30:05+00:00",
  "data": {
    "client": {
      "id": "6710c0ffee0000000000abcd",
      "tin": "1000000000",
      "company_name": "Client LLC",
      "status": "entity_created",
      "created_at": "2026-10-01T09:00:00+00:00",
      "entity_created_at": "2026-10-03T12:30:00+00:00"
    }
  }
}

Verify and handle it. Every Taxstar webhook is signed the same way; see Webhooks for the header. Match the event to your own record by data.client.id, and make the handler idempotent: a retry can deliver the same event twice.

Receive a client event
// Node.js (Express) - receive a client event
import crypto from "crypto";

app.post(
  "/taxstar/webhooks",
  express.raw({ type: "application/json" }), // keep the raw, unparsed body
  (req, res) => {
    const header = req.headers["x-taxstar-signature"] || "";
    const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
    const expected = crypto
      .createHmac("sha256", process.env.TAXSTAR_WEBHOOK_SECRET)
      .update(parts.t + "." + req.body.toString()) // signed string is "{t}.{raw body}"
      .digest("hex");
    const given = Buffer.from(parts.v1 || "");
    const ok =
      given.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(expected), given);
    if (!ok) return res.sendStatus(400);

    const { event, data } = JSON.parse(req.body.toString());
    if (event === "client.onboarded") {
      // data.client.id is your pre-registered client; data.client.tin is its TIN.
      // Events can repeat (a retry after a slow answer), so make this idempotent.
      markClientOnboarded(data.client.id, data.client.tin);
    }
    res.sendStatus(200); // any 2xx acknowledges; answer within 10 seconds
  }
);

What the company's owner is told

When a company is linked to you, whether it registers after your request or was already registered and free, Tax Star emails its owner once. The email names your application, says it was given editor access to manage the company on Tax Star and that it was connected successfully, and asks the owner to contact Tax Star support if they did not allow it. It also states that the company and all its billing will be managed by the partner.

It carries only your application's name and the company's name; the client's email, TIN and your credentials are never in it. Use a clear application name: it is what the owner reads. A company that disputes a link is handled by Tax Star support. Partners and resellers each have their own email.

What Tax Star records

Every pre-register call, whatever its outcome, is written to your application's activity log with the action partner_pre_register and a result: pending, linked_existing_entity, refused_duplicate, refused_reserved, refused_already_linked or invalid_request. The entry carries ids only (the client request and, for a link or a refusal, the company), never the TIN, the email or the company name. You can see it under Your apps.

Tax Star also keeps an audit trail of each request, step by step: when it was made and through what, whether it was refused and why, when and how a company was linked to it and the rule that allowed it, what access the link gave, and whether the owner was emailed. It holds the TIN and ids, never an email address or a name. Support uses it to answer how, when and why a TIN is connected to a partner.

Look up a response

ResponseMeaning
400 invalid_requestThe tin is not ten digits starting with 1 (a 15-digit TRN included), or the email is not valid. details names the field; the value you sent is not echoed back.
409 entity_existsThe company is registered and a reseller, a partner or an OAuth application is already attached. The response never says which, or who.
409 tin_reservedAnother partner or reseller requested this TIN first. No client is stored for your request.
409 (no code)You already requested this TIN. Look it up in your client list instead.
403 account_suspendedThe account is suspended. Reading still works.
403 insufficient_scopeThe wrong kind of app for these endpoints. See the audience notes at the top of the page.
429Rate limit exceeded. Wait for Retry-After seconds.

Glossary

TermMeaning
TINTaxpayer Identification Number: the first ten digits of a company's TRN. It is what identifies a client in this API, in a field called tin.
TRNTax Registration Number: the company's 15-digit number. Keep its first ten digits to get the TIN. The pre-register API never takes or returns it.
EntityA company registered on Tax Star: it holds the invoices, the ERP connection and the members.
PendingA request status: you reserved the TIN, and no company with it has been registered yet.
entity_createdA request status: a company with the client's TIN is registered and linked to you. entity_created_at says when.
not_linkedA legacy duplicate request status: another earlier partner or reseller request reserved the same TIN and was linked instead.
linked_existing_entitytrue in the response when the company was already registered, and free, when you asked, so you were linked at once. false means your request is pending.
AttachedA company is attached when a reseller, a partner or an OAuth application is already linked to it. A company with something attached cannot be linked to you: 409 entity_exists.
EditorA member role on a company. An editor can connect an ERP, validate and send invoices, and manage members other than admins. A linked partner is added as one.
LimitedA reseller's access: it is never added to the company's members and holds no role. It sees only the counts of invoices sent, received and failed.
client.onboardedThe one webhook event of this flow: a client you pre-registered is now registered and linked to you.

Common questions

QuestionAnswer
Can I pre-register a company that is already registered?Yes, if nothing is attached to it and no partner or reseller has already reserved its TIN. The first accepted request is linked at once and gets 201 with linked_existing_entity: true. If the TIN is reserved by another partner or reseller, you get 409 tin_reserved; if the company is attached to another account or OAuth app, you get 409 entity_exists.
Why did I get 409 entity_exists?A company with that TIN is already registered and a partner, reseller or OAuth application is attached. Tax Star does not say which or who. If another partner or reseller has reserved the TIN first, the separate response is 409 tin_reserved. Contact Tax Star support if you think the attachment or reservation is wrong; every outcome is recorded.
Can multiple partners or resellers request the same TIN?No. One TIN can be linked to only one partner or reseller across Tax Star, even while the company is not registered. The first accepted request reserves it; later requests receive 409 tin_reserved. For legacy duplicate pending requests, the earliest request is linked and the others appear as not_linked.
I sent a 15-digit TRN and got 400. What now?Send the first ten digits instead, in tin. The pre-register API identifies a company by its TIN only.
Is the company's owner told when I am linked?Yes. When a company is linked to you, Tax Star emails its owner once, naming your application (or, for a reseller's dashboard request, you), and asks them to contact Tax Star support if they did not allow it. Partners and resellers each have their own email.
Do I see the company's invoices?Yes. A linked company adds your account to its members as an editor, so through your application you can use the company: read it, connect an ERP, validate and send invoices, and manage members other than admins. The list and the webhooks still give you the counts. Linking gives write access, so only pre-register clients you are authorised to manage.
What happens to a pending request when the company registers?The reserved request becomes entity_created and a client.onboarded webhook is sent to its owner. No competing partner or reseller is linked. The company is matched by its TIN alone, never by the email you gave.
Why was my client not accepted?Every pre-register call is recorded in your application's activity with its outcome (pending, linked_existing_entity, refused_duplicate, refused_reserved, refused_already_linked or invalid_request) and ids only, never the TIN or email. That is how Tax Star support can answer the question for any request.
Can I pre-register any company?Only clients you are authorised to manage. Linking gives your account an editor role, which can send invoices as the company. One TIN can be linked to only one partner or reseller across Tax Star: the first accepted request reserves it, even while the company is not registered, and later requests receive 409 tin_reserved. The owner is emailed and can remove your editor access, and Tax Star keeps a record of every link.
What if I already have a role on the company, or own it?A role you already hold is left as it is: you are not added twice and not promoted. If your account owns the company you are not added, because you already manage it.

What each app can call

API callAny approved app (a partner)A reseller account's app
POST /oauth/token — get an access tokenYesYes
POST /oauth/partners/pre-register — pre-register a clientYesNo
GET /oauth/partners/pre-register/clients and /{client_id} — clients, entity id and countsYesNo
/oauth/entities, /oauth/users, /oauth/invoices — the rest of the OAuth APIYesNo
/oauth/webhooks — subscribe to invoice events and to client.onboardedYesNo
/oauth/reseller/* — the reseller APINoYes

The partner endpoints share the rate limits of the rest of the OAuth API, counted per app, and the same token lifetimes. A suspended account cannot pre-register a client; reading and webhooks management of existing ones still work.

Clients, status and counts

statusMeaning
pendingRequested, and no company with this TIN has been registered since. activity is null: there is nothing to count.
entity_createdA company with the client's TIN is registered and linked to you, whether it already existed when you asked or was registered afterwards. entity_created_at says when, and activity carries its counts.

The counts are the ones the Tax Star admin panel shows for that company: documents that reached the Peppol network (sent, received) and send attempts whose latest status is a failure (failed). A linked client also includes its entity_id; no other entity data is exposed in the client list. The company's own invoices and data are available through your application once you are its editor. totals on the list covers every client you have pre-registered, not only the current page.

One client, in GET /oauth/partners/pre-register/clients
{
  "id": "6710c0ffee0000000000abcd",
  "tin": "1000000000",
  "email": "client@example.com",
  "company_name": "Client LLC",
  "notes": "",
  "status": "entity_created",
  "created_at": "2026-10-01T09:00:00+00:00",
  "entity_created_at": "2026-10-03T12:30:00+00:00",
  "entity_id": "6710c0ffee0000000000abcf",
  "activity": { "sent": 42, "received": 17, "failed": 2 }
}

Webhooks, in short

A partner is an ordinary OAuth app, so there is one event to know about and no separate endpoints: client.onboarded is in the normal webhook event list. The full walkthrough, with the delivery diagram, the signature check and a receiver, is above; creating, testing and rotating a webhook is on Webhooks.

The endpoints are on Partner clients. The same flow for resellers is on Reseller accounts. Roles and what an app may reach are on Roles and permissions. Tokens are on Authentication.