---
title: "Integration Tokens"
description: "How a machine gets its own long-lived credential for your catalog — issuing an offline token, naming it, seeing what it did, and revoking it when the system it belonged to is retired."
canonical_url: "https://openepcis.io/docs/digital-data-management/connector-tokens"
last_updated: "2026-09-02T11:48:21.207Z"
---

## Why a machine needs its own credential

An ERP that publishes products into your catalog every night has to prove who it
is on every call. The obvious shortcut is to give it a person's username and
password, and it is the wrong one for three reasons that all arrive later rather
than sooner: passwords expire and the nightly job stops at 3 a.m.; people leave
and their account goes with them; and when something publishes a wrong price,
the audit trail names a colleague who was asleep.

An **integration token** is the alternative. It belongs to a system rather than
to a person, it is named after that system, and — issued the way the next
section describes — it can be revoked on its own without touching anything
else. It is what every OpenEPCIS connector — the UnoPim route, the ERPNext
route, the Odoo add-on — authenticates with.

## What the token actually is

It is an **offline token**: an OAuth refresh token issued with the
`offline_access` scope. The distinction matters, because it is the reason the
thing works at all. An ordinary refresh token dies with the session that
created it, so an integration built on one runs for a few minutes after you set
it up and then stops — a failure mode that looks like a bug in the connector
and is not.

The integration never sends the offline token to the catalog. It trades it at
the identity provider for a short-lived access token, and uses that. So the
long-lived value sits in one place — the integration's own configuration — and
the credential that travels on every request expires in minutes.

## Issuing one

Open **Profile** in DDM and use the integration-tokens card. It takes you
through the identity provider's own login and consent screen, and back to a
page that shows the token.

Three things about that trip are deliberate and worth knowing before you take
it:

**You will be asked to consent, even though you are already logged in.** That
prompt is not a redundancy. An offline token outlives the session that made it,
and quite possibly the person who made it, so the person is asked once,
explicitly, to allow that.

**The token is shown exactly once.** Nothing stores it — not DDM, not the
resolver. What remains afterwards is the row in your list: the name, when it
was issued, when it was last used. If you close the page before copying the
value, nothing is broken and nothing is lost except two clicks: issue another
one, and revoke the one you abandoned.

**Name it after the system, not after yourself.** `unopim-production` and
`odoo-nightly` are useful names; `sven-token-2` is not, because the whole point
of a per-system token is that you can retire one system without wondering what
else you are breaking. You can supply the name when you start the flow, or
rename it afterwards. A flow started without a name stores none — nothing
fails, the row simply comes out unnamed, and naming it later is the remedy.

## Issuing a second one: log out first

This is the one step that surprises people, and getting it wrong quietly
removes the property the whole mechanism exists for.

A token is bound to a **session** at the identity provider, not to the click
that produced it. Run the flow twice in the same browser without logging out
and the second run reuses the session you are already in: you get two
different token values that belong to **one** session. The list then shows a
single row, not two — and revoking that row invalidates **both** tokens at
once. One system retired, two integrations down.

So when you need a token per system, end the session between issues: log out,
or run the second flow in a private window. Each run then gets its own
session, the list shows one row per token, and revoking one leaves the other
running.

If you already have two tokens from one session, nothing is broken — they
work. They are simply not independently revocable, and the fix is to issue
fresh ones with separated sessions and revoke the shared row afterwards.

## Putting it into a connector

The token is one property. In the connectors' configuration it is the
`target.offline-token` of a connection:

```properties
openepcis.connectors.connections.pim1.target.offline-token=<the value>
```

In a cluster the same key arrives as an environment variable — upper-cased,
every non-alphanumeric character turned into `_`:

```text
OPENEPCIS_CONNECTORS_CONNECTIONS_PIM1_TARGET_OFFLINE_TOKEN
```

Put it in a Kubernetes secret mounted into the deployment, not in a properties
file on disk. See [How a connector works](/docs/connectors/how-a-connector-works)
for the rest of a connection's configuration.

A token is bound to the realm, the client and the person who issued it. That
has a consequence people meet when they build a second environment: **a token
issued on one environment does not work on another.** Copying the value from a
staging deployment into production, or the reverse, produces an authentication
failure that looks like a typo. Issue one per environment.

## Seeing what a token has been doing

The list on the Profile page is metadata only — it never shows a token value
again. Each row carries the name, the issue time and the **last used** time,
and that last column is the useful one. A token that has never been used points
at an integration that was configured but never started. A token that stopped
being used weeks ago points at a system that was retired without anyone
revoking its credential, which is exactly the kind of key that should not still
be lying around.

You only ever see your own tokens. The list is anchored on the account that
issued them, so a colleague's integration tokens are not yours to inspect or
revoke — and yours are not theirs.

One row means one session, which is why the section above matters: if two
tokens share a session, this list cannot tell them apart, because there is
nothing in it that distinguishes them.

## Revoking

Revoking deletes the offline session behind the token — and *the session* is
the unit, which is the second reason to keep one session per token. The
integration's next attempt to trade its token for an access token fails with
`invalid_grant`, and it will keep failing until someone gives the integration a
new token. That is the
intended behaviour and it is abrupt on purpose: a credential you decided to
withdraw should stop working, not wind down.

So revoke when the system is retired, when a token has been exposed, or when
the list shows one nobody can account for. What you should *not* do is revoke a
token to "refresh" it — there is nothing to refresh, and the integration stops
until it is reconfigured.

## When the card is not there

The feature is off unless a deployment turns it on, and it needs the identity
provider's client to be configured for it. If the Profile page shows no
integration-tokens card, or the flow answers that it is unavailable, the
deployment has not enabled it — that is an administrator's task, not something
a retry will fix.

Two failures have a specific cause worth naming:

- **"This is not an offline token."** The flow refuses to hand over a refresh
token that is not an offline one, rather than giving you a credential that
works for minutes. The client needs the `offline_access` scope allowed.
- **An expired or unrecognised request.** A started flow has to be finished
within a short window — ten minutes, by default. Leaving the consent screen
open over lunch and then continuing means starting again.
