The JoNet API is a set of simple JSON endpoints. Every order is paid from your JoNet wallet, and your API commission is taken off at the moment you buy.
Create a JoNet account, then apply for API access from Upgrade Account in your dashboard.
Once approved, open API Keys in your dashboard and generate your key. Keep it secret -- it can spend your wallet.
Fund your wallet, and set your Webhook URL so we can tell you when a pending order finishes.
Call the catalog endpoints (plans, packages, discos), store them, and start selling.
Authentication
Send your API key in the Authorization header on every request. A Bearer prefix is also accepted.
Authorization: YOUR_API_KEY
Keys are valid for one year. We remind you before expiry; an expired key returns 106. Generating a new key stops the old one immediately.
Use https only -- plain http is refused (104).
Up to 50 requests a minute per key (429 after that).
You can restrict your key to your own server IP addresses on your dashboard. A request from any other IP is refused, and its responseCode is the IP we saw.
Making requests
Base URL: https://jonet.com.ng/api_live/v1/
POST endpoints take a JSON body with Content-Type: application/json (max 64 KB). GET endpoints take query parameters.
Always read responseCode in the JSON body. Don't decide on the HTTP status alone -- some refusals come back with HTTP 404 and a JSON body.
Store the catalogs (data plans, TV packages, add-ons, discos) and refresh them regularly rather than on every order.
Customer ID
Every purchase needs a customer_id: your own unique reference for the order. It must:
start with today's date and hour in Lagos time (WAT, GMT+1) as YYYYMMDDHH -- we recommend adding the minute too (YYYYMMDDHHmm);
be 10 to 20 characters, using only letters, digits and -;
be unique on your account. Reusing one returns JO105.
The customer_id is how you find an order again with Requery. Keep it with your order.
Handling responses
Every purchase reply ends up in one of three states. Use the Treat as column in the table below.
Success (200) -- delivered.
Pending (201, 202, 203, 2031, 2032, JO105) -- your wallet was charged and the order is still being completed. It will succeed or be refunded. We send your webhook when it finishes, or you can call Requery.
Failed -- not delivered. If you were charged, the reply says refunded (JO106, JO107, JO1061) and the money is back in your wallet.
Never resend a Pending order with a new customer_id -- you may deliver it twice. The same goes for a timeout or no reply at all: Requery the customer_id first. We only refund when the vendor clearly confirms a failure; anything unclear stays Pending until it is settled.
Response codes
Any code not listed here should be treated as Failed.
Code
Meaning
Treat as
200
Successful. On Requery, 200 only means the order was found -- read status for its state.
Success
201, 202, 203
Processing / status not final yet. The order is charged; it completes or is refunded shortly. Wait for the webhook or Requery.
Pending
2031, 2032
Electricity: duplicate at the vendor (2031) or unknown status (2032). Not refunded -- Requery.
Pending
JO105
This customer_id was already used on your account. Requery that customer_id -- do not resend with a new one.
Pending
JO106, JO107, JO1061
Failed. Your wallet has been refunded.
Failed
002, 10114
Your wallet could not be charged. Nothing was taken.
Failed
101
No Authorization header found.
Failed
102
Invalid API key (Get Wallet answers an invalid key with 103).
Failed
103
Wrong request method (e.g. GET on a POST endpoint), or invalid authorization: the token does not belong to this key's account, or the account is not an API account.
Failed
104
Insecure connection -- use https.
Failed
105
Unable to process: unknown plan / package / disco, or an invalid amount or quantity.
Failed
106
This API key has expired. Generate a new one on your dashboard.
Failed
107
This API key has been paused, or the account has been suspended.
Failed
108, 109
Account not found, no vendor set up for this service, or busy -- try again.
Failed
110, 1014, 424
Insufficient balance (424 is also used for "cannot be done right now", e.g. out of stock, Airtime to Cash errors).
Failed
1005, 1050
Unable to process request. If it persists, contact support.
Failed
1015
This account is not eligible to make purchases.
Failed
JO101
This service is temporarily switched off (or not supported for this provider).
Failed
JO102
Missing or invalid request fields.
Failed
JO103
Wrong phone number format (must start with 0).
Failed
JO104
Wrong customer_id format (see Customer ID below).
Failed
JO1051
Wrong network name (Airtime).
Failed
JO109
No webhook URL set on your account.
Failed
JO110
Transaction not found.
Failed
JO119
Transaction not completed yet (webhook can only be resent for a finished order).
Failed
JO121
Your wallet balance is negative. Fund your wallet first.
Failed
JO123
The meter or smartcard could not be verified with the vendor. You were not charged.
Failed
409
Another action on your wallet is in progress -- try again.
Failed
429
Too many requests (more than 50 a minute per key) -- slow down.
Failed
500
Temporary error -- try again.
Failed
Your IP address
This request came from an IP address not on your allowed list.
Failed
INVALID_JSON, PAYLOAD_TOO_LARGE
The body is not valid JSON, or is larger than 64 KB.
Failed
SERVICE_UNAVAILABLE
The same customer_id is already being processed (sent twice at the same time), or the service is briefly unavailable -- wait a moment, then requery that customer_id before retrying.
Failed
Webhook
When an order that came back Pending reaches its final state, we POST this JSON to the webhook URL set on your dashboard (Settings › Webhook). Use an https URL.
To check it really came from us, compare the Authorization header with your API key.
type is Data, Airtime, E-Pin, Digital tv or ENERGY BILL; status is Successful or Failed (Failed = refunded).
Note the field names under Accounts: bankName holds the account number and accountNumber holds the bank name. This is long-standing behaviour that existing integrations rely on, so it is kept as is.
price = face value per pin; amount_charged = what you pay per pin; available: false = out of stock (stock is a count only for plans sold from JoNet's own stock).
Wrong OTP (try again or resend), not enough airtime, no longer pending, vendor refused, or "needs manual review" (airtime received -- support credits it)
409
Wallet busy
Notes
Payout = converted amount × the rate, minus NGN 50 per NGN 5,000 shared.
Sell tickets to events on JoNet, or list your own events. Ticket purchases are instant: tickets are issued (or nothing is charged) in the same call -- there is no Pending state.
GET
Get Events
https://jonet.com.ng/api_live/v1/get_events.php
Approved events that haven't ended, soonest first (up to 100), with their ticket categories.
Example request
curl -X GET 'https://jonet.com.ng/api_live/v1/get_events.php' \
-H 'Authorization: YOUR_API_KEY'
Buy tickets for your customer from your wallet, at the full ticket price.
Body (JSON)
Field
Description
event_idstring
Required
From Get Events.
tier_idnumber
Required
A ticket category id of that event.
quantitynumber
Required
1 to 10.
customer_idstring
Required
Your unique reference -- see Customer ID. Sending the same one again returns the original tickets; nothing is bought twice. Reusing it for a DIFFERENT order (other event, tier, quantity or holder) is refused with 409.
holder_namestring
Required
Your customer's name -- printed on every ticket and seen by the organizer.
holder_phonestring
Required
Your customer's phone number.
holder_emailstring
Required
Your customer's email.
attendeesarray
Optional
Optional: [{name, phone, email}, ...] with exactly one entry per ticket, to put a different person on each ticket (then holder_* can be left out).
{
"message": "Transaction not found",
"responseCode": "JO110",
"status": "Failed"
}
Common errors
Code
When
JO102
Send customer_id or ticket_code
JO110
Not found, or not yours
JO119
The order is still processing
Notes
You can see tickets you bought, and every ticket of an event you organize. A Failed order charged nothing. Ticket entries carry the same fields as in Purchase Ticket.
curl -X GET 'https://jonet.com.ng/api_live/v1/get_event_fees.php' \
-H 'Authorization: YOUR_API_KEY'
Responses
Successful
{
"responseCode": "200",
"status": "Successful",
"paid_ticket_percent": 5,
"free_event_fee": 500,
"terms": "Paid tickets: JoNet keeps 5% of each ticket sold; you receive the rest. Free events (every ticket free): a one-off listing fee of NGN 500.00 is taken from your wallet when you submit the event, and refunded if the event is not approved. Attendees pay nothing."
}
Notes
Paid tickets: JoNet keeps paid_ticket_percent of each sale; you receive the rest (the buyer pays the ticket price).
Free events (every category free): one flat free_event_fee per event, taken from your wallet when you submit it and refunded if it isn't approved. Attendees pay nothing.
The rates in force when you submit an event stay with that event. 0 = no fee.
POST
Create Event
https://jonet.com.ng/api_live/v1/create_event.php
Submit your own event for sale. It starts pending and is sold once JoNet approves it.
Body (JSON)
Field
Description
name, venue, descriptionstring
Required
Event details.
start_date, end_datestring
Required
YYYY-MM-DD.
start_time, end_timestring
Required
HH:MM (24-hour).
category[], price[]array
Required
One pair per ticket category; a blank or 0 price = free.
picturefile
Required
JPEG, PNG, GIF or WEBP, up to 5 MB.
idempotency_keystring
Optional
Your unique reference for this submission. Sending the same one again (e.g. after a lost reply) returns the first result -- no second event or listing fee. Reusing it for a different event is refused.
Example request
curl -X POST 'https://jonet.com.ng/api_live/v1/create_event.php' \
-H 'Authorization: YOUR_API_KEY'