Education Purchase
LIVEQuick Summary
Purchase exam checker pins for WAEC, NECO, or NABTEB. You can order multiple pins at once by specifying the quantity. Pins are delivered asynchronously via webhook — each pin generates an individual transaction reference for tracking purposes.
Endpoint
Send a POST request with the purchase details in the request body.
https://pairgate.com/api/v1/education/purchase
https://pairgate.com/api/v1/test/education/purchase
Authentication
This request requires a valid Bearer token.
| Header | Value | Description |
|---|---|---|
Authorization |
Bearer {token}
|
Your unique API authorization token |
Content-Type |
application/json
|
Required for POST requests |
Body Parameters
All parameters are required. Send them as JSON in the request body.
| Parameter | Type | Required | Description |
|---|---|---|---|
provider_id |
string | Yes | Provider slug (e.g. waec, neco, nabt) |
quantity |
integer | Yes | Number of pins to purchase |
reference |
string | Yes | Your unique reference for this transaction (8–100 characters) |
Code Examples
Choose your preferred language below. Replace the parameters and YOUR_API_KEY with your actual values.
// Purchase Exam Pins
$response = Http::withHeaders([
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
])->post('https://pairgate.com/api/v1/education/purchase', [
'provider_id' => 'waec',
'quantity' => 2,
'reference' => 'my-education-order-001',
]);
$result = $response->json();
dd($result);
curl -X POST "https://pairgate.com/api/v1/education/purchase" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider_id": "waec",
"quantity": 2,
"reference": "my-education-order-001"
}'
const axios = require('axios');
const purchaseExamPins = async () => {
try {
const response = await axios.post(
'https://pairgate.com/api/v1/education/purchase',
{
provider_id: 'waec',
quantity: 2,
reference: 'my-education-order-001'
},
{
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
}
}
);
console.log(response.data);
} catch (error) {
console.error(error.response?.data || error.message);
}
};
purchaseExamPins();
import requests
import json
url = "https://pairgate.com/api/v1/education/purchase"
headers = {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"provider_id": "waec",
"quantity": 2,
"reference": "my-education-order-001"
}
try:
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
print(json.dumps(data, indent=2))
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
HttpClient client = HttpClient.newHttpClient();
String jsonBody = "{"
+ "\"provider_id\": \"waec\","
+ "\"quantity\": 2,"
+ "\"reference\": \"my-education-order-001\""
+ "}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://pairgate.com/api/v1/education/purchase"))
.header("Authorization", "Bearer YOUR_API_KEY")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
try {
HttpResponse response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
}
Example Request Body
{
"provider_id": "waec",
"quantity": 2,
"reference": "my-education-order-001"
}
Response Format
Success Response (200)
Purchase accepted, pins being generated:
{
"code": 200,
"status": "success",
"data": {
"status": true,
"message": "Successfully purchased 2 exam pin(s). Processing will be completed shortly.",
"reference": "my-education-order-001",
"total_amount": 2000.00,
"unit_price": 1000.00,
"quantity": 2,
"exam_type": "WAEC",
"details": [
{
"reference": "TRXEXAM20260615104818CQ3",
"pin": null
},
{
"reference": "TRXEXAM20260615104818CQ4",
"pin": null
}
]
}
}
Test Response (200)
{
"code": 200,
"status": "success",
"data": {
"test_mode": true,
"message": "This is a test — no balance deducted.",
"request": {
"provider_id": "waec",
"quantity": 2,
"reference": "my-education-order-001"
},
"balance": 20000.00
}
}
Success Response Fields
| Field | Type | Description |
|---|---|---|
status |
boolean | true if successful |
message |
string | Status description |
reference |
string | Your original reference |
total_amount |
float | Total amount charged |
unit_price |
float | Price per pin |
quantity |
integer | Number of pins purchased |
exam_type |
string | Exam body name |
details |
array | Individual transaction references for each pin |
Webhook — Pin Delivered
When each pin is generated, a webhook is sent to your configured URL. One webhook per pin. Ensure your webhook endpoint is set up to receive these POST notifications.
Webhook Payload
{
"event": "waec.purchase",
"reference_code": "TRXEXAM20260615104818CQ3",
"status": "successful",
"message": "Waec purchase completed.",
"item": "Waec Exam Checker Pin",
"amount": 1000.00,
"pin": "WAEC-12345-67890-ABCDE",
"completed_at": "2026-06-15T10:05:00+01:00"
}
Webhook Fields
| Field | Type | Description |
|---|---|---|
event |
string | Purchase event type |
reference_code |
string | Matches one of the references from the details array |
status |
string | successful when the pin is ready |
message |
string | Status description |
item |
string | Exam body name |
amount |
float | Amount charged per pin |
pin |
string | The exam checker pin. For WAEC, format: PIN_NUMBER<=>SCRATCH_CARD_SERIAL (e.g. 12114234543543<=>WRN12345678). For NECO/NABTEB: just the pin number. |
completed_at |
string | Timestamp when the pin was generated |
12114234543543<=>WRN12345678 where the format is
PIN_NUMBER<=>SCRATCH_CARD_SERIAL. For NECO and NABTEB,
the pin is delivered as just the pin number with no <=> separator.
Error Responses
The following errors may occur when calling this endpoint.
| Status | Code | Description |
|---|---|---|
| 422 | Invalid provider |
Provider not found or inactive |
| 422 | Unknown exam type |
Exam type not recognised |
| 422 | Insufficient balance |
Wallet balance is too low |
| 422 | Duplicate reference |
This reference has already been used |
| 401 | Missing API key |
No Bearer token provided |
| 401 | Invalid API key |
Token does not match any active key |
| 403 | Suspended |
API key or account has been suspended |
| 429 | Rate limited |
Too many requests |