# Retrieve an event

The normalized event, identical in shape whichever provider caused it.

## Endpoint

GET https://api.veyqo.org/api/v1/events/{id}

Server secret key required. Scope: events:read. The key determines the project and environment; no project-selection header is accepted.

## Path and query parameters

| Parameter | Location | Required | Description |
| --- | --- | --- | --- |
| id | path | Yes | Use the ID returned when this resource was created. |

## Example request

Examples use illustrative IDs and provider URLs. Replace path variables with IDs returned by your own requests. Never paste a live secret into client code or a public tool.

### cURL

```bash
curl --request GET "https://api.veyqo.org/api/v1/events/$EVENT_ID" \
  --header "Authorization: Bearer $VEYQO_SECRET_KEY" \
  --header "Accept: application/json"
```

### Node.js

```javascript
// Server-side Node.js 18+. Save operation.idempotencyKey before a write.
const response = await fetch(`https://api.veyqo.org/api/v1/events/${encodeURIComponent(process.env.EVENT_ID)}`, {
  method: 'GET',
  headers: {
  "Authorization": `Bearer ${process.env.VEYQO_SECRET_KEY}`,
  "Accept": "application/json"
},
});
const result = await response.json();
if (!response.ok || !result.success) {
  console.error(response.status, result.error?.code, response.headers.get('X-Request-Id'));
  throw new Error(result.error?.message ?? 'Veyqo request failed');
}
console.log(result.data);
```

### PHP

```php
<?php
// Server-side PHP with the cURL extension. Persist $operationIdempotencyKey.
$curl = curl_init('https://api.veyqo.org/api/v1/events/'.rawurlencode(getenv('EVENT_ID')).'');
curl_setopt_array($curl, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer '.getenv('VEYQO_SECRET_KEY'),
    'Accept: application/json'
  ],
]);
$body = curl_exec($curl);
if ($body === false) throw new RuntimeException(curl_error($curl));
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);
$result = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
if ($status >= 400 || !($result['success'] ?? false)) {
  throw new RuntimeException($result['error']['code'] ?? 'Veyqo request failed');
}
print_r($result['data']);
```

### Python

```python
# pip install requests; run on your server.
import os, requests
from urllib.parse import quote

response = requests.request('GET', f'https://api.veyqo.org/api/v1/events/{quote(os.environ["EVENT_ID"], safe="")}',
    headers={
  "Authorization": 'Bearer ' + os.environ['VEYQO_SECRET_KEY'],
  "Accept": "application/json"
},
    timeout=30)
result = response.json()
if not response.ok or not result.get('success'):
    raise RuntimeError(f"{response.status_code} {result.get('error')} request_id={response.headers.get('X-Request-Id')}")
print(result['data'])
```

## Successful response · 200

HTTP success means the request was accepted. A pending payment or refund is not completed merely because the response is 200 or 201.

```json
{
  "success": true,
  "data": {
    "id": "evt_01M6EB66SC61Q3RQYAN0693DGM",
    "type": "payment.succeeded",
    "created_at": "2026-11-02T12:00:00+00:00",
    "environment": "sandbox",
    "data": {
      "payment_id": "pay_01M6EB66SC61Q3RQYAN0693DGM",
      "reference": "ORDER-1001",
      "amount": 50000,
      "amount_refunded": 0,
      "currency": "NGN",
      "status": "succeeded",
      "customer": {
        "email": "customer@example.com",
        "name": "Ada Lovelace"
      },
      "provider": {
        "name": "paystack",
        "reference": "provider_reference_example"
      },
      "metadata": {
        "order_id": "1001"
      }
    }
  }
}
```

## Response fields

| Field in data | Type | Presence | Description |
| --- | --- | --- | --- |
| id | string | Required | Deduplication key: the same ID as Veyqo-Event-Id. |
| type | string | Required | Normalized event type; see the webhook guide. |
| created_at | string | Required | Event creation timestamp. Format: date-time. |
| environment | string | Required | Values: sandbox, live. |
| data | EventData | Required | Payment snapshot when this event was recorded. Refund events also include refund_id and refund_amount. |
| data.payment_id | string | Required | Payment ID to retrieve and verify. |
| data.reference | string | Required | Merchant reference. |
| data.amount | integer | Required | Original minor-unit amount. |
| data.amount_refunded | integer | Required | Minor-unit refunded amount. |
| data.currency | string | Required | See the JSON example. |
| data.status | string | Required | Values: created, pending, processing, succeeded, failed, cancelled, refunded, partially_refunded. |
| data.customer | object | Required | See the JSON example. |
| data.customer.email | string | Required | See the JSON example. |
| data.customer.name | string | Optional | See the JSON example. |
| data.provider | object | Required | See the JSON example. |
| data.provider.name | string | Required | Provider slug. |
| data.provider.reference | string \| null | Required | Provider reference. |
| data.metadata | object \| array | Required | Your non-sensitive JSON metadata. Objects are recommended; do not store secrets or card details. |
| data.refund_id | string | Optional | Present for refund events. |
| data.refund_amount | integer | Optional | Present for refund events, in minor units. |
| data.refund_status | string | Optional | Present for refund events. Values: pending, succeeded, failed. |
| data.reason | string \| null | Optional | Present for refund events. |

## Errors and recovery

| Code | HTTP | Recovery | Retry policy |
| --- | --- | --- | --- |
| INVALID_API_KEY | 401 | Use a current kc_test_ or kc_live_ secret key on /api/v1. Use a pk_test_ or pk_live_ publishable key only on browser checkout. | After fixing credentials. |
| INSUFFICIENT_PERMISSION | 403 | Grant the required scope or use a suitably scoped key. Dashboard organization roles and staff permissions are separate. | After permission changes. |
| RATE_LIMIT_EXCEEDED | 429 | Honor Retry-After, use bounded exponential backoff with jitter, and reduce concurrent requests. Read X-RateLimit headers. | Yes, after Retry-After; preserve write idempotency. |
| METHOD_NOT_ALLOWED | 405 | Use the method documented for the endpoint, such as POST for verification and refunds. | After fixing the method. |
| INTERNAL_ERROR | 500 | Record X-Request-Id, timestamp and sanitized request context. Reconcile unknown payment outcomes before retrying a write; contact support if it persists. | Safe reads: bounded backoff. Writes: reconcile first. |
| RESOURCE_NOT_FOUND | 404 | Check the ID, project and environment. Do not guess another project’s IDs. An inactive payment link is also not-found. | No blind retry. |

- [Complete error catalogue](https://veyqo.org/docs/errors): Every Veyqo error code, including which surface emits it and how to recover.
