# Payment Dispute Evidence

When a player disputes a payment, the payment processor asks Aghanim to prove that the purchase was legitimate and delivered. Aghanim compiles an evidence package from the data it already has, and the S2S Dispute Evidence API lets your game server add the game-side facts the platform cannot know: whether the purchased goods were consumed, where they were used, and details about the player's account in the game.

## How it works

1.  **Dispute opened** - A player files a dispute with their payment method. Aghanim sends a [`payment.dispute` webhook](https://docs.aghanim.com/webhooks/payment-events.md) for the payment.
2.  **You submit evidence** - Your server calls the Dispute Evidence API with consumption facts, player details, and supporting files.
3.  **Evidence package sent** - Aghanim includes your evidence in the package submitted to the payment processor.
4.  **Dispute resolved** - The outcome arrives as a [`payment.succeeded` (won) or `payment.chargeback` (lost) webhook](https://docs.aghanim.com/webhooks/payment-events.md#the-dispute-object) carrying the `dispute` object.

## Prerequisites

*   **API Key**: Required for authenticating your S2S API requests.
*   **Payment number**: The endpoints identify the payment by its payment number, which is the `receipt_number` field of the [payment webhook](https://docs.aghanim.com/webhooks/payment-events.md#the-eventdata-schema) payload.

## Submit evidence

Create or update dispute evidence for a payment:

```text
PUT https://api.aghanim.com/s2s/v1/payments/{payment_number}/dispute_evidence
```

### Request schema

All fields are optional. The request is a partial update: only the fields present in the JSON body are written, fields you omit keep their stored values, and a field sent as `null` is cleared. Repeated calls merge into the same evidence record.

| Field | Type | Description | Required |
| --- | --- | --- | --- |
| `purchase_used` | `boolean` | Whether the purchased goods were consumed. | No |
| `used_at` | `number` | Unix timestamp when the purchased goods were consumed. | No |
| `usage_id` | `string` | Your identifier of the event where the goods were consumed, e.g. a match, session, or quest ID. Max 512 characters. | No |
| `comment` | `string` | Free-form note for support. Max 4096 characters. | No |
| `player_attributes` | `PlayerAttributes` | Player fields stored on the player profile, not on the payment. | No |

#### The `PlayerAttributes` object schema

| Field | Type | Description | Required |
| --- | --- | --- | --- |
| `registered_at` | `number` | Unix timestamp when the player registered in the game. It may predate the creation of the player's Aghanim account. | No |
| `first_login_ip` | `string` | IP address of the player's first login in the game, as reported by the game. Max 64 characters. | No |
| `is_verified` | `boolean` | Whether the player's account is verified on the game side. | No |

The `player_attributes` fields are the same attributes your server can return from the [`player.verify` webhook](https://docs.aghanim.com/webhooks/verify-player.md#the-attributes-object); use whichever channel fits your integration.

### Example request

```bash
curl -X PUT https://api.aghanim.com/s2s/v1/payments/2409051289614565/dispute_evidence \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_API_KEY>' \
  -d '{
        "purchase_used": true,
        "used_at": 1725634567,
        "usage_id": "match_8f3a1c",
        "comment": "Crystals were spent on a legendary chest",
        "player_attributes": {
          "registered_at": 1609459200,
          "first_login_ip": "203.0.113.7",
          "is_verified": true
        }
    }'
```

The response is the [DisputeEvidence object](#the-disputeevidence-object).

## Retrieve evidence

Return the stored dispute evidence for a payment, including the player fields:

```text
GET https://api.aghanim.com/s2s/v1/payments/{payment_number}/dispute_evidence
```

The response is the [DisputeEvidence object](#the-disputeevidence-object). The endpoint responds with `404` when no evidence has been submitted for the payment yet.

## Upload an evidence file

Attach a supporting file (a screenshot, a log extract, a receipt) to the payment's evidence:

```text
POST https://api.aghanim.com/s2s/v1/payments/{payment_number}/dispute_evidence/files
```

### Request schema

The file is sent as base64 inside a JSON body, not as multipart form data:

| Field | Type | Description | Required |
| --- | --- | --- | --- |
| `file_name` | `string` | Original file name, kept as part of the stored file ID. Max 255 characters. | **Yes** |
| `file_type` | `string` | MIME type of the file, e.g. `application/pdf`. Defaults to `application/octet-stream`. | No |
| `file_base64_content` | `string` | File content encoded as base64. Up to roughly 10 MB of binary data. | **Yes** |

The content must be valid base64 without line breaks or whitespace, otherwise the request fails with `422`.

### Example request

```bash
curl -X POST https://api.aghanim.com/s2s/v1/payments/2409051289614565/dispute_evidence/files \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_API_KEY>' \
  -d '{
        "file_name": "consumption-log.pdf",
        "file_type": "application/pdf",
        "file_base64_content": "JVBERi0xLjQKJ..."
    }'
```

The endpoint responds with `201` and the [DisputeEvidence object](#the-disputeevidence-object) including the updated `file_ids`. Each upload appends to the list; there is no way to remove an uploaded file via the API, so contact support if a file was attached by mistake.

## The DisputeEvidence object

Both endpoints and the file upload return the full evidence record:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | Unique identifier of the evidence record, prefixed with `pde_`. |
| `payment_number` | `string` | Payment number the evidence belongs to. |
| `purchase_used` | `boolean\|null` | Whether the purchased goods were consumed. |
| `used_at` | `number\|null` | Unix timestamp when the purchased goods were consumed. |
| `usage_id` | `string\|null` | Your identifier of the event where the goods were consumed. |
| `file_ids` | `string[]\|null` | Identifiers of the uploaded evidence files. |
| `comment` | `string\|null` | Free-form note for support. |
| `modified_at` | `number\|null` | Unix timestamp of the last update, `null` until the record is updated. |
| `player_attributes` | `PlayerAttributes` | Player fields from the player profile; all fields are `null` when unknown. |

#### The `PlayerAttributes` object in responses

| Field | Type | Description |
| --- | --- | --- |
| `registered_at` | `number\|null` | Unix timestamp when the player registered in the game. |
| `last_active_at` | `number\|null` | Unix timestamp of the player's last activity on the platform. Read-only. |
| `first_login_ip` | `string\|null` | IP address of the player's first login in the game. |
| `is_verified` | `boolean\|null` | Whether the player's account is verified on the game side. |

### Example response

```js
{
  "id": "pde_eFgYpxryeKXpLKfmZstI",
  "payment_number": "2409051289614565",
  "purchase_used": true,
  "used_at": 1725634567,
  "usage_id": "match_8f3a1c",
  "file_ids": ["fl_a1b2c3_consumption-log.pdf"],
  "comment": "Crystals were spent on a legendary chest",
  "modified_at": 1725638167,
  "player_attributes": {
    "registered_at": 1609459200,
    "last_active_at": 1725630000,
    "first_login_ip": "203.0.113.7",
    "is_verified": true
  }
}
```

## Error responses

| HTTP Status | Endpoint | Description |
| --- | --- | --- |
| `401` | All | Missing or invalid API key. |
| `404` | All | The payment number is unknown for your game. |
| `404` | `GET` | No dispute evidence has been submitted for the payment yet. |
| `404` | `PUT` | `player_attributes` fields were sent, but the payment has no player to store them on. |
| `422` | `PUT`, `POST` | Validation error: a field exceeds its length limit or `file_base64_content` is not valid base64. |

---
Source: https://docs.aghanim.com/advanced/dispute-evidence