# Reconciliation

The Fynex reconciliation surface lets sellers compare what Fynex has recorded as settled funds against what actually arrived in their bank account. Each reconciliation record represents a settlement deposit event with three amounts: **reported** (what the processor claimed), **expected** (what Fynex calculated), and **actual** (what was received).

> [!NOTE]
> **GraphQL only — cookie session required.** All reconciliation queries are on `POST /dashboard/graphql` and require permission `RECONCILIATIONS_READ` on the session. See [GraphQL Authentication](https://api.fynex.ai/payments-api/v2/docs#tag/graphql-auth).

---

## Use cases

- **Daily settlement file matching** — pull all reconciliation records for the previous day and compare reported vs actual amounts to detect shortfalls.
- **Dispute investigation** — fetch a single `reconciliation(id)` to see the full fact list (fees reported vs expected) for a specific deposit event.
- **Dispute reserve calculation** — use `reconciliationStatistics` to get aggregate counts of pending, successful, and discrepancy records over a date range.

---

## GraphQL operations

| Operation | Signature | Permission |
|-----------|-----------|------------|
| List reconciliations | `reconciliations(limit: Int, offset: Int, status: String, dateFrom: Time, dateTo: Time): ReconciliationConnection!` | `RECONCILIATIONS_READ` |
| Get single record | `reconciliation(id: Int!): ReconciliationSettlementDeposit` | `RECONCILIATIONS_READ` |
| Aggregate statistics | `reconciliationStatistics(dateFrom: Time, dateTo: Time): ReconciliationStatistics!` | `RECONCILIATIONS_READ` |

> [!NOTE]
> Raw processor-side settlement records (the underlying settlement file contents) are not part of the seller-facing API. Their shape follows whichever processor is configured for the seller, so the field names are not a stable public contract and are not suitable to build against. The reconciliation surfaces documented on this page are the supported way to reconcile.

---

## Type reference

### `ReconciliationConnection`

The list query returns a connection type rather than a plain array:

| Field | Type | Description |
|-------|------|-------------|
| `items` | `[ReconciliationSettlementDeposit!]!` | Page of reconciliation records |
| `totalCount` | `Int!` | Total number of records matching the filter |

### `ReconciliationSettlementDeposit`

The primary reconciliation entity — one record per settlement deposit event.

| Field | Type | Description |
|-------|------|-------------|
| `id` | `Int!` | Record ID |
| `bankingTransferReference` | `String!` | Reference of the corresponding bank transfer |
| `depositAmountReported` | `Float!` | Amount the processor reported in the settlement file |
| `depositAmountExpected` | `Float!` | Amount Fynex calculated based on processed payments |
| `depositAmountActual` | `Float!` | Amount actually received in the banking deposit |
| `status` | `String!` | Reconciliation status (`pending`, `success`, `discrepancy`, etc.) |
| `createdAt` | `Time!` | When the record was created |
| `updatedAt` | `Time!` | Last update time |
| `genericSettlementReport` | `ReconciliationSettlementReport` | Linked processor settlement report |
| `bankingDeposit` | `ReconciliationBankingDeposit` | Linked banking deposit |
| `facts` | `[ReconciliationFact!]!` | Line-item fee discrepancy details |

### `ReconciliationSettlementReport`

| Field | Type | Description |
|-------|------|-------------|
| `id` | `Int!` | Report ID |
| `paymentProviderCode` | `String!` | Processor identifier |
| `operationalMode` | `String!` | `demo` or `live` |
| `periodStart` | `Time` | Settlement period start |
| `periodEnd` | `Time` | Settlement period end |
| `bankingTransferReference` | `String!` | Bank transfer reference |
| `fundingPaymentCurrency` | `String!` | Currency of the funding payment |
| `totalFundingPaymentAmountReported` | `Float!` | Total reported funding amount |
| `totalFundingPaymentAmountExpected` | `Float!` | Total expected funding amount |
| `totalPurchaseFeeAmountReported` | `Float!` | Total purchase fees as reported |
| `totalOtherFeeAmountReported` | `Float!` | Other fees as reported |
| `status` | `String!` | Report status |

### `ReconciliationFact`

Each fact represents a single line-item discrepancy found during reconciliation.

| Field | Type | Description |
|-------|------|-------------|
| `id` | `Int!` | Fact ID |
| `factType` | `String!` | Category of discrepancy |
| `discrepancyPlace` | `String!` | Where in the flow the discrepancy was detected |
| `feeAmountReported` | `Float!` | Fee amount from processor file |
| `feeAmountExpected` | `Float!` | Fee amount Fynex expected |
| `feeAmountActual` | `Float!` | Fee amount actually applied |
| `normalizedRecord` | `ReconciliationNormalizedRecord` | The payment transaction this fact relates to |

### `ReconciliationStatistics`

| Field | Type | Description |
|-------|------|-------------|
| `successCount` | `Int!` | Reconciliations with no discrepancy |
| `pendingCount` | `Int!` | Reconciliations not yet matched |
| `discrepancyCount` | `Int!` | Reconciliations where amounts do not match |
| `totalReportedAmount` | `Float!` | Sum of all reported deposit amounts |
| `totalActualAmount` | `Float!` | Sum of all actual deposit amounts |

---

## Code samples

#### curl

```bash
# Step 1 — login
curl -sc cookies.txt \
  -X POST https://api.fynex.ai/api/v1/login/dashboard \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com", "password": "your_password"}'

# Step 2 — list reconciliations for a date range
curl -b cookies.txt \
  -X POST https://api.fynex.ai/dashboard/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "query Recons($dateFrom: Time, $dateTo: Time, $limit: Int, $offset: Int) { reconciliations(dateFrom: $dateFrom, dateTo: $dateTo, limit: $limit, offset: $offset) { totalCount items { id status depositAmountReported depositAmountExpected depositAmountActual bankingTransferReference createdAt } } }",
    "variables": {
      "dateFrom": "2025-01-01T00:00:00Z",
      "dateTo": "2025-01-31T23:59:59Z",
      "limit": 50,
      "offset": 0
    }
  }'
```

#### JavaScript

```js
const BASE = 'https://api.fynex.ai';

async function login(email, password) {
  const res = await fetch(`${BASE}/api/v1/login/dashboard`, {
    method: 'POST',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password }),
  });
  if (!res.ok) throw new Error(`Login failed: ${res.status}`);
}

async function gql(query, variables = {}) {
  const res = await fetch(`${BASE}/dashboard/graphql`, {
    method: 'POST',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, variables }),
  });
  const { data, errors } = await res.json();
  if (errors?.length) throw new Error(errors[0].message);
  return data;
}

await login('you@example.com', 'your_password');

const { reconciliations } = await gql(
  `query Recons($dateFrom: Time, $dateTo: Time, $limit: Int, $offset: Int) {
     reconciliations(dateFrom: $dateFrom, dateTo: $dateTo, limit: $limit, offset: $offset) {
       totalCount
       items {
         id
         status
         depositAmountReported
         depositAmountExpected
         depositAmountActual
         bankingTransferReference
         createdAt
         facts {
           factType
           discrepancyPlace
           feeAmountReported
           feeAmountExpected
         }
       }
     }
   }`,
  {
    dateFrom: '2025-01-01T00:00:00Z',
    dateTo: '2025-01-31T23:59:59Z',
    limit: 50,
    offset: 0,
  }
);

console.log(`Total: ${reconciliations.totalCount}`);
for (const r of reconciliations.items) {
  const diff = r.depositAmountActual - r.depositAmountExpected;
  console.log(`${r.id} [${r.status}] diff: ${diff.toFixed(2)}`);
}
```

#### Python

```python
import requests

BASE = "https://api.fynex.ai"
session = requests.Session()

session.post(
    f"{BASE}/api/v1/login/dashboard",
    json={"email": "you@example.com", "password": "your_password"},
).raise_for_status()

query = """
  query Recons($dateFrom: Time, $dateTo: Time, $limit: Int, $offset: Int) {
    reconciliations(
      dateFrom: $dateFrom
      dateTo: $dateTo
      limit: $limit
      offset: $offset
    ) {
      totalCount
      items {
        id
        status
        depositAmountReported
        depositAmountExpected
        depositAmountActual
        bankingTransferReference
        createdAt
        facts {
          factType
          discrepancyPlace
          feeAmountReported
          feeAmountExpected
          feeAmountActual
        }
      }
    }
  }
"""

resp = session.post(
    f"{BASE}/dashboard/graphql",
    json={
        "query": query,
        "variables": {
            "dateFrom": "2025-01-01T00:00:00Z",
            "dateTo": "2025-01-31T23:59:59Z",
            "limit": 50,
            "offset": 0,
        },
    },
)
resp.raise_for_status()
result = resp.json()
if "errors" in result:
    raise RuntimeError(result["errors"][0]["message"])

data = result["data"]["reconciliations"]
print(f"Total records: {data['totalCount']}")

for r in data["items"]:
    diff = r["depositAmountActual"] - r["depositAmountExpected"]
    print(f"ID {r['id']} [{r['status']}]: diff={diff:+.2f}, ref={r['bankingTransferReference']}")
    if r["facts"]:
        for fact in r["facts"]:
            print(f"  Fact: {fact['factType']} @ {fact['discrepancyPlace']}")
```

---

## Reconciliation workflow

1. **Fetch daily statistics** with `reconciliationStatistics(dateFrom, dateTo)` to get a quick health check: `successCount`, `pendingCount`, and `discrepancyCount`.

2. **List pending and discrepancy records** by passing `status: "pending"` or `status: "discrepancy"` to `reconciliations`. Focus your investigation on these.

3. **Inspect a specific record** with `reconciliation(id)` to get the full `facts` list showing where fees diverge between reported, expected, and actual.

4. **Cross-reference with wallet entries** — each settled payment that contributed to the deposit will have a corresponding `WalletEntry` with `kind: INCOMING_PAYMENT_PROCESSED`. Compare `depositAmountActual` against the sum of wallet credit entries for the same period.

5. **Escalate discrepancies** to your Fynex account manager if `depositAmountActual` differs from `depositAmountExpected` and the `facts` list does not explain the gap.

---

## Filtering tips

- `dateFrom` and `dateTo` accept RFC 3339 timestamps (`2025-01-15T00:00:00Z`).
- Use `status` to filter by reconciliation outcome. Common values include `pending`, `success`, and `discrepancy` — confirm the exact values with your Fynex contact as the list may expand.
- Combine `limit` + `offset` for pagination. The `totalCount` field tells you how many pages to expect.

## See also

- **[GraphQL Authentication](https://api.fynex.ai/payments-api/v2/docs#tag/graphql-auth)** — Obtain the dashboard_session cookie required for all GraphQL calls.
- **[Wallets](https://api.fynex.ai/payments-api/v2/docs#tag/wallets)** — Inspect wallet ledger entries that correspond to reconciled settlements.
- **[Payment Lifecycle](https://api.fynex.ai/payments-api/v2/docs#tag/payment-lifecycle)** — Understand the status transitions a payment goes through before it settles.
