# Credit card transactions and the statement cycle

Credit card data moves more than checking-account data: rows are restated as a
bill closes, instalments are re-dated to the cycle they belong to, and pending
purchases are confirmed. This page explains what that does to transaction `id`s
and how to reconcile through it.

## What identifies a transaction across syncs

On every sync Pluggy matches the transactions it just collected against the ones
it already stored, using a single key:

- **The institution's own transaction id**, when the connection provides one.
- Otherwise, **a value derived from the transaction's `description`, `amount`,
  `date`, and its position among identical transactions on the same day.**

The consequence is the important part:

| Outcome | What you receive | Does the `id` survive? |
| --- | --- | --- |
| The key matches | `transactions/updated` | **Yes** — the stored transaction is updated in place. |
| The key does not match | `transactions/deleted` for the old row, `transactions/created` for the new one | **No** — the transaction comes back with a new `id`. |

So a change in the **description, amount or date** of an already-stored
transaction is, by construction, a different transaction. That is why a
restatement around a bill closing can arrive as a delete followed by a create
rather than as an update.

<Callout variant="warning" title="The ids cannot be preserved, and nothing links the old id to the new one">
There is no option to keep the original `id` across a delete and re-create, and
the `transactions/deleted` payload carries only the ids that were removed — no
field points from a deleted transaction to its replacement. Any integration that
treats the Pluggy `id` as a permanent primary key for a credit card transaction
will drift.
</Callout>

## What to anchor on instead

**`providerId`** — the institution's own transaction id — is the only stable
provider-side identifier Pluggy exposes, and it is **returned for Open Finance
connectors only**. On a direct connector it is always `null`, whatever the
institution is.

This matters when the same institution is reachable both ways. Itaú, for
example, has both direct connectors and Open Finance ones: a card connected
through the direct connector never carries `providerId`, the same card connected
through Open Finance does.

If you have no `providerId`, reconcile on the transaction's attributes —
`date` + `amount` + `description` — as described in
[Transactions](/docs/products/transactions), and keep your own mapping from those
attributes to your internal record rather than from the Pluggy `id`.

See [Open Finance vs Direct: field differences](/docs/connections/open-finance-vs-direct-fields)
for the full list of fields that only one connection type fills.

## Practical handling

- **Process `transactions/deleted` idempotently.** A transaction can disappear in
  one sync and come back in a later one, so a delete is not necessarily final.
- **Re-fetch rather than infer.** After any transaction event, read the current
  state with [`GET /transactions`](/reference/transactions-list) for the affected
  account instead of reconstructing it from the event stream.
- **Expect the bill window to be wide.** Some institutions assign an instalment
  to a bill months ahead of the purchase, so a credit card transaction dated in
  the future is not necessarily an error.

<Callout variant="info" title="If transactions disappear and do not come back">
Whether a row stopped being returned by the institution or was dropped during the
sync is not something you can tell apart from the API response alone. Open a
support ticket with the `itemId`, the account, and the dates involved, and we can
check the execution — see [Reporting issues](/docs/connections/reporting-issues).
</Callout>