FN SHOP developer docs
Base URL: https://fnitem.shop/api/v3/service/ · Your API key is on the API Key page.
Authentication #
Send your API key as a header or query param:
- header
X-Api-Key: <key>, or - query
?api_key=<key>
The API detects your site’s domain from the request and binds your key on first use. Additional domains or subdomains can be enabled for your key. Your key only sees that owner’s accounts, categories and orders.
Plan limits #
Each key is on a plan with a number of requests per term. A term is the period you paid for — a month, or the three, six or twelve you bought at once — and the count starts over when you renew. verify_license is never counted, so checking your key costs nothing.
| Plan | Per month | Requests per term |
|---|---|---|
| Basic | $25 | 5,000 |
| Standard | $50 | 20,000 |
| Unlimited | $100 | no limit |
Doubling the price quadruples the requests, so the bigger plan is cheaper per call.
A call past the plan is refused before it does anything, and is not counted:
{
"result": false,
"code": "plan_limit_reached",
"description": "The standard plan allows 20,000 requests per term, and this key has used them. Renew to start a new term.",
"plan": "standard",
"limit": 20000,
"used": 20000,
"resets_at": 1794689329,
"resets_in": 432000
}resets_at is when the term ends — the key’s expiry. Renewing before then starts a new term at once. groups_status returns plan with used, remaining and resets_at, so a storefront can see how close it is before a call is refused, and your API key page shows the same.
Request & response #
Call https://fnitem.shop/api/v3/service/{action} (GET or form-encoded POST). Check result and code in the JSON body; HTTP 200 alone does not mean success. A typical response is:
{ "result": true, "code": "ok_code", "description": "", ... }Gift lifecycle #
| Action | Method | Params | Purpose |
|---|---|---|---|
verify_license | GET/POST | — | Validate the API key; returns user_id + plan. |
resolve_account | GET/POST | username, mode | Resolve a Fortnite username (or psn: / xbl: / nintendo: prefixed) to an Epic account id. |
friend_add | GET/POST | receiver, receiver_id?, category?, account_id?, limit? | Friend the buyer from every account (or one category) and report when gifting becomes possible. |
friend_status | GET/POST | receiver, receiver_id? | Where the buyer stands with each of your accounts: friends and giftable, friends but inside the 48 hours, request pending, or none. |
gift_check | GET/POST | offerId, receiver, receiver_id?, category, account_id? | Check whether a gift can be sent from the category's pool or a specific sender. |
gift_send | GET/POST | offerId, receiver, receiver_id?, order_id, category, account_id? | Send the gift from the category's pool or a specific sender. Records the order. |
Shop #
| Action | Method | Params | Purpose |
|---|---|---|---|
shop | GET/POST | offerId?, giftable?, search?, limit?, lang? | The live item shop. Pass lang to localize item names, for example fr, ar or zh-Hans. |
Categories (per owner) #
| Action | Method | Params | Purpose |
|---|---|---|---|
category_list | GET/POST | — | List your categories with account counts + the default. |
category_create | POST | slug, name | Create a category. slug: 2-40 chars of a-z 0-9 _ ; "all" reserved. |
category_delete | POST | slug, reassign_to? | Delete a category; if it holds accounts, pass reassign_to=<slug>. |
Accounts #
| Action | Method | Params | Purpose |
|---|---|---|---|
accounts_list | GET/POST | category? | List your accounts (id, name, category, cached V-Bucks/gifts). |
accounts_data | GET/POST | — | Live per-account V-Bucks + gifts (queries Epic). |
account_assign | POST | account_id | account_ids, category | Move existing account(s) into a category. |
account_link | POST | code, category | Link a new Epic account (Android authorization code) into a category. |
account_remove | POST | account_id | Unlink an account (DB row only). |
Dashboards #
| Action | Method | Params | Purpose |
|---|---|---|---|
inventory | GET/POST | — | Aggregate per category: counts, V-Bucks, free gift slots. No identifiers. |
groups_status | GET/POST | live? | Per-account detail per category, in gifting priority order. live=1 refreshes first. |
Orders #
| Action | Method | Params | Purpose |
|---|---|---|---|
order_history | GET/POST | type?, status?, receiver?, sender?, offer?, order_id?, category?, from?, to?, q?, sort?, dir?, limit?, offset? | Every order you have: gifts sent, Battle Pass gifts, and purchases on your own accounts. |
category #
For gift_check/gift_send, category is one of your category slugs, or all (any account), or omitted (default). Only accounts whose group matches are used; an empty pool returns no_accounts_in_group.
FN SHOP Connect #
Add a Connect with FN SHOP button to your shop. The customer signs in with Telegram and chooses which Epic account to connect. Once they approve, your shop can see their Epic display name and ID, see pending requests from your shop’s accounts, and send or accept friend requests. Epic credentials stay with FN SHOP.
Before you start Have an active API key and an HTTPS callback on a domain allowed by that key. Make every request from your server using POST, X-Api-Key, and form-encoded fields. In the examples, set FN_API_KEY to your key and replace shop.example with your domain.
1. Get a login link · 2. Handle the callback · 3. Get a token · 4. Use the connection
1. Get a login link #
POST https://fnitem.shop/api/v3/service/connect_start
When the customer clicks your button, generate a random state and a PKCE verifier. Save both with your callback URL in the customer’s server-side session. Send the SHA-256 challenge shown below, then redirect the browser to the returned login_url.
SHOP_HOST=shop.example
CALLBACK=https://shop.example/fnshop/callback
STATE=$(openssl rand -hex 16)
VERIFIER=$(openssl rand -hex 32)
CHALLENGE=$(printf '%s' "$VERIFIER" | openssl dgst -sha256 -binary |
openssl base64 -A | tr '/+' '_-' | tr -d '=')
curl -sS -X POST 'https://fnitem.shop/api/v3/service/connect_start' \
-H "X-Api-Key: $FN_API_KEY" \
--data-urlencode "svr=$SHOP_HOST" \
--data-urlencode "redirect_uri=$CALLBACK" \
--data-urlencode "scope=friends.manage account.read" \
--data-urlencode "state=$STATE" \
--data-urlencode "code_challenge=$CHALLENGE" \
-d response_type=code -d code_challenge_method=S256
Callback URL and login-link details
The callback must use HTTPS with no credentials, query string or fragment. The consent page displays its origin. To display a specific URL on that origin, pass shop_url with the same URL restrictions.
On success, the response includes result: true, code: login_link_created, login_url and expires_in: 300. The link can be opened once within five minutes. The customer then has 15 minutes to finish signing in and approving access.
2. Handle the callback #
On approval, the browser returns to your callback with ?code=…&state=…. If the customer declines, it returns with ?error=access_denied&state=…. In either case, match state against the saved session and consume the pending attempt once. Exchange the code only after validation succeeds.
3. Get a connection token #
POST https://fnitem.shop/api/v3/service/connect_token
Exchange the callback code once within two minutes. Use the same API key, exact callback URL and original verifier from step 1.
curl -sS -X POST 'https://fnitem.shop/api/v3/service/connect_token' \
-H "X-Api-Key: $FN_API_KEY" \
--data-urlencode "svr=$SHOP_HOST" \
-d grant_type=authorization_code \
--data-urlencode "redirect_uri=$CALLBACK" \
--data-urlencode "code=$CALLBACK_CODE" \
--data-urlencode "code_verifier=$VERIFIER"
Save the returned access_token on your server as CONNECT_TOKEN for this customer. This is a FN SHOP token. It expires after 30 days, and the customer can revoke it sooner under Connected Apps.
4. Use the connection #
Use the same API key and add Authorization: Bearer <access_token>. The token identifies the approved customer account. For send and accept, target_account_id is the other account’s Epic ID.
Read the connected account #
POST https://fnitem.shop/api/v3/service/connect_account
Returns account_id and display_name for the approved account. Requires account.read.
Show cURL example
curl -sS -X POST 'https://fnitem.shop/api/v3/service/connect_account' \
-H "X-Api-Key: $FN_API_KEY" \
-H "Authorization: Bearer $CONNECT_TOKEN" \
--data-urlencode "svr=$SHOP_HOST"List pending incoming requests #
POST https://fnitem.shop/api/v3/service/connect_friends_incoming
Returns account_ids for pending requests from accounts linked to the owner of your API key. An empty array means none matched. Requires account.read.
Show cURL example
curl -sS -X POST 'https://fnitem.shop/api/v3/service/connect_friends_incoming' \
-H "X-Api-Key: $FN_API_KEY" \
-H "Authorization: Bearer $CONNECT_TOKEN" \
--data-urlencode "svr=$SHOP_HOST"Send a friend request #
POST https://fnitem.shop/api/v3/service/connect_friend_send
Returns friend_request_processed when Epic processes the action. An existing incoming request from that target may be accepted.
Show cURL example
curl -sS -X POST 'https://fnitem.shop/api/v3/service/connect_friend_send' \
-H "X-Api-Key: $FN_API_KEY" \
-H "Authorization: Bearer $CONNECT_TOKEN" \
--data-urlencode "svr=$SHOP_HOST" \
--data-urlencode "target_account_id=$TARGET_EPIC_ID"Accept a friend request #
POST https://fnitem.shop/api/v3/service/connect_friend_accept
Pass the ID of one of your shop’s accounts that has already sent the customer a request. Returns friend_request_accepted after FN SHOP confirms the friendship.
Show cURL example
curl -sS -X POST 'https://fnitem.shop/api/v3/service/connect_friend_accept' \
-H "X-Api-Key: $FN_API_KEY" \
-H "Authorization: Bearer $CONNECT_TOKEN" \
--data-urlencode "svr=$SHOP_HOST" \
--data-urlencode "target_account_id=$TARGET_EPIC_ID"Connect errors and recovery #
Common errors
- Login link rejected. Check the allowed domain, HTTPS callback and PKCE fields.
invalid_grantorinvalid_token. Verify the original callback, verifier and API key. Start again if the code or token expired, or the customer revoked it.insufficient_scope. Existing action-only connections need a new login link withfriends.manage account.readfor the read endpoints.incoming_request_not_found. Check that the target is one of your shop’s accounts and its request is still pending.- 429 or an unconfirmed friend action. Back off on 429. For
friend_outcome_unknownorfriendship_not_confirmed, check the result in Fortnite before retrying.
Check the HTTP status and response body. Key and plan errors can return HTTP 200 with result: false. Token exchange returns access_token on success and uses error for an invalid grant.
gift_check #
Checks an offer against the recipient and your category pool. It sends no gift, but can send friend requests from accounts visited during the check. Use friend_status for a read-only friendship lookup.
Parameters match gift_send, except that order_id is not used. Pass account_id to check only one sender; see Selecting a sender.
| Success field | Type | Meaning |
|---|---|---|
receiver_id | string | Resolved recipient Epic ID. |
sender_id | string | The sender found eligible at check time. |
eligible_accounts | string[] | Currently one matching sender ID, not a list of every eligible account. |
category | string | Category used by the check. |
final_price | integer or null | Price quoted by Epic, in V-Bucks. |
A successful check returns eligible_to_receive_gift. It does not reserve a sender, balance, gift slot or price. It does not echo offerId, mainId or original catalog price; obtain those from shop. Refusal codes and the reasons breakdown are described under gift_send.
gift_send #
Sends one offer to one recipient. Eligibility is checked again at send time. Use GET or form-encoded POST; a JSON request body is not supported. Always inspect result and code, even when HTTP status is 200.
Parameters #
| Parameter | Required | Meaning |
|---|---|---|
offerId | Yes | Current offer ID from shop; not the cosmetic mainId. |
receiver | Yes | Recipient display name, optionally platform-prefixed. Still required when sending receiver_id. |
receiver_id | No | Recipient Epic ID, 32 hex characters. An unusable ID falls back to resolving receiver; see Identifying the buyer. |
category | No | Your category slug, or all. Omitted, empty or unknown uses your default category. |
account_id | No | Pin a sender by its 32-character Epic ID. This is a different account from receiver_id. |
order_id | No | Your order reference, preserved for matching history. Not an idempotency key. |
Selecting a sender #
Without account_id, the API chooses the first eligible sender in the current category priority order. Ties can select different accounts between requests. With account_id, only that account is checked and used. It must belong to you and the selected category; an invalid, unavailable or ineligible sender is refused without fallback.
To keep the same sender, pass the same account_id to both gift_check and gift_send. A check does not reserve the account. There is no atomic membership snapshot, lock, reservation or check token binding the two calls. Category changes can cause a pinned send to be refused, while an unpinned send may choose a different account.
Prices and quotes #
The API checks the current price against the price cached by gift_check and can refuse an increase with final_price_not_match. This is not a binding quote or a guaranteed maximum-spend authorization. A catalog-out-of-date response may trigger a retry using Epic’s revised price. No quote token or contractual quote-validity period is provided.
Response fields #
A confirmed send returns result: true and code: gift_sent_successfully.
| Field | Type | Meaning |
|---|---|---|
track_id | string | Attempt reference; matches orders[].id in order_history. |
sender_id | string | Epic ID of the account that executed the attempt. |
sender | string | Sender display name. Use sender_id for a stable identity. |
receiver_id | string | Recipient Epic ID. |
order_id | string or null | Your original reference, or null if omitted. |
category | string | Category used for this send. |
final_price | integer or null | Reported V-Bucks price on success. |
date | integer | Success timestamp, Unix seconds. |
An execution refusal or unknown outcome also includes track_id, sender_id, sender and order_id. Validation and eligibility refusals before execution do not necessarily include them.
Refusals #
Handle result: false as unsuccessful. Common codes include invalid_sender_account, account_not_in_group, no_accounts_in_group, receiver_account_not_found, not_eligible_to_receive_gift, final_price_not_match and plan_limit_reached.
not_eligible_to_receive_gift can include reasons, mapping reason names to arrays of sender IDs: friendship_not_found, friendship_not_passed, vbucks, gifts, token_expired, throttled or other. It also includes giftable_at (Unix seconds or null) and giftable_in (seconds or null) when a friendship wait can be calculated.
Other checks can return catalog_out_of_date, purchase_not_allowed, receiver_owns_item_from_bundle or receiver_will_not_accept_gifts. Execution refusals can return Epic’s full dotted error code. Unknown codes must not be treated as success.
Order references, retries and unknown outcomes #
order_id is a reference, not a durable idempotency key. Repeated or concurrent requests are not deduplicated, and reusing the reference with changed parameters is not rejected on that basis. No at-most-once guarantee or idempotency-retention period is provided.
A missing or malformed Epic confirmation returns gift_outcome_unknown with outcome: unknown. No completed order is created for that response. Keep the attempt unresolved and reconcile it before retrying; a lost HTTP reply is also an unknown outcome.
Query order_history with your order_id, recipient and offer to investigate. A missing history entry does not prove that no gift occurred. There is no dedicated unknown-outcome reconciliation endpoint or guaranteed history-retention window; contact support with the order and track references if the outcome remains unclear.
In history, sender is the sender’s Epic ID, id is the track reference, and order_id is your shop reference. offer, main_id and price identify the recorded offer, cosmetic and V-Bucks cost. These fields let you identify the gift without relying on the category name.
For friendship age, pending requests and the 48-hour wait, see friendship timestamps.
Identifying the buyer #
Every gifting call takes receiver, an Epic username. That works until the buyer renames — then the stored value stops resolving, and they are told their username is wrong for an account that is perfectly fine.
gift_check, gift_send and friend_add therefore also return receiver_id, the resolved Epic account id, and accept it back on the next call. An account id never changes, so a shop that stores one keeps working across a rename — and skips the lookup, so the call is slightly faster too.
| Field | Notes |
|---|---|
receiver | Still required, even when you send an id — it is what gets resolved if the id is missing or unusable. |
receiver_id | Optional. Used as-is when it looks like an Epic id: exactly 32 hex characters. Anything else — truncated, stale, padded, or not hex — is ignored and receiver is resolved instead, so a bad stored value degrades to the old behaviour rather than being handed to Epic as a recipient. |
Returned on refusals as well as on success, so a shop can capture the id from the first call it makes even when that call says no. friend_add also returns it as receiver for callers written before this existed; both fields carry the same value.
friend_add #
Fortnite only allows a gift once both accounts have been friends for 48 hours. Call friend_add as soon as a buyer enters their name — at the top of checkout, or when they add to cart — so the wait is already running by the time they pay. Calling it again is safe: accounts that are already friends are skipped rather than sent a second request.
Leave category out and the request goes from every account you have. That is usually what you want: any account might end up being the one that gifts, and befriending only one leaves the rest unable to deliver. Pass a category to restrict it to that pool.
| Field | Notes |
|---|---|
receiver | Required. Epic username, or an account id, or a console name with a platform prefix: psn:, xbl: or nintendo: (xb:, xbox:, ns:, switch:, ps5: are accepted as the same thing). |
category | Optional. Restrict to one pool. Omitted → every account. |
account_id | Optional. Pin a single sender, so a returning buyer stays with the account that already befriended them. |
limit | Optional. Caps how many accounts are contacted in one call. Unlimited by default — the whole pool is contacted together, so size no longer costs time. |
The reply summarises the whole pool, then breaks it down per account:
| Field | Notes |
|---|---|
code | friend_request_sent, already_friends, partially_sent (some accounts failed), friend_request_failed (all did), or friends_lists_full — every account that would have had to send is at Epic's friends cap, so nothing was attempted. That one is for the store owner, not the buyer. A refusal that is the buyer’s own answers with its own code instead: receiver_not_accepting_friends, receiver_friends_list_full, receiver_request_list_full, receiver_account_not_found or receiver_account_banned — the buyer’s Epic account is banned, which no shop account can work around. |
sent, already_friends, failed, skipped | Counts across the pool. skipped accounts were set aside before anything was sent because their friends list is full; they are neither a success nor a failure, and one that already knows the buyer can still gift them. |
can_gift_now | true if any account can gift right now — check this before offering checkout. |
giftable_at | Soonest unix time any account becomes able to gift. null while every request is still unaccepted. |
giftable_in | Seconds until that moment, for a countdown. |
accounts_total, accounts_tried, truncated | truncated is true when the pool was bigger than limit; the rest are picked up on the next call. |
accounts[] | Per account: account_id, displayName, status (sent / already_friends / skipped / failed), friends_since, giftable_at, can_gift_now, error when it failed, and reason when it was skipped. |
A pending request has giftable_at: null: Epic starts the 48 hours when the buyer accepts, not when you send. Tell them to accept, then poll this or gift_check. One account failing doesn't stop the others — check accounts[] for the detail.
friend_status #
Where a buyer stands with each of your accounts, without sending anything. Call it before offering checkout, or to show a returning buyer how long is left.
| Field | Notes |
|---|---|
ready, waiting, pending, none | Counts across the pool: friends for 48 hours and able to gift; friends but still inside the 48 hours; a request sent and not yet accepted; no relationship at all. |
min_hours | The friendship Epic requires, in hours (48). |
soonest_hours | Hours until the first waiting account becomes able to gift; null when none is waiting. |
accounts[] | Per account: account_id, displayName, state (friends / pending / none), created, hours of friendship so far, giftable, ready_in_hours. |
Takes receiver and receiver_id like friend_add, and reads the pool of the same category. A buyer with ready above zero has cleared the friendship wait on at least one account; balance, gift capacity and offer eligibility still need checking.
Friendship timestamps #
accounts[].created is Epic’s timestamp string for the existing friendship, or null, read from Epic on each status request. It is normally ISO 8601 UTC and is not a locally recorded request-sending time. Pending outgoing requests are checked separately and have state: pending with created: null.
After unfriend/re-friend, the API uses the timestamp Epic subsequently returns; no independent reset or propagation guarantee is provided. giftable describes clearing the configured friendship wait, normally 48 hours, not complete eligibility for an offer. Use gift_check for the offer check. Missing or malformed upstream data must not be treated as proof of readiness.
shop #
Returns the items currently in the shop, ready to list in a storefront. Filter with offerId, giftable=1|0, search (name substring) or limit.
| Field | Notes |
|---|---|
offerId | Pass this to gift_check / gift_send. It changes every time the shop rotates, so don't use it to identify an item. |
mainId | The item's permanent id (CID_…, EID_…). Use this one to recognise the same item across rotations. |
name, description | For a bundle, the bundle's own name and blurb. |
kind | What the entry is: bundle, jam_track, instrument, car, lego_kit or item. Use this to include or exclude a whole class of item — type is null for jam tracks, cars, LEGO kits, instruments and V-Bucks packs, so it can't be filtered on. A bundle stays bundle even when it is made of music. |
type, rarity, series | e.g. outfit / Epic / Icon Series. series is null for ordinary items. |
price, regularPrice | V-Bucks. They differ when the item is discounted. |
giftable | false for items Epic won't allow as a gift (Battle Passes, V-Bucks packs). |
image | Item artwork URL. |
colors | The item's colour pair, for matching the in-game card background. |
inDate, outDate | How long the item stays in the shop (ISO-8601). |
Also returned: uid (changes when the shop rotates, so you can skip a refresh), count and total. shop_unavailable means shop data could not be read.
groups_status #
Every category with its accounts, in the order gifts actually use them — the drag order you set on the Accounts page. A gift goes out from the first eligible account in the list, so this is the order to show if you display a pool anywhere.
| Field | Notes |
|---|---|
position | 1-based place in the gifting queue. Label rows with this, not with sort_order. |
sort_order | The raw stored value. It repeats freely and is 0 for every account never dragged, so it means nothing on its own. |
position can be null, and the list then falls back to name order. Treat null as "no priority to show" rather than implying one. In the (ungrouped) bucket position is cosmetic: those accounts aren't in any category, so they never gift.
order_history #
Every order placed against your accounts, whatever placed it. Alias: orders.
type says what kind of order a row is:
type | Meaning |
|---|---|
gift | Sent to a receiver. |
purchase | Bought on your own account. No receiver. |
api_gift | Sent to a receiver, placed through an API key. |
battlepass | A Battle Pass gift. |
gift and api_gift are both gifts and differ only in how they were placed, so the filter takes a list: type=gift,api_gift gives every gift and nothing you bought for yourself.
Orders record what kind they were, not which app placed them, so there is no filter for that.
Filters are optional and combine. Send all, or leave a filter out, to ignore it — so you can pass the same parameter every time and vary only its value.
| Parameter | Notes |
|---|---|
type | One of the four above. Comma-separated for more than one. |
status | Usually completed. |
receiver | Epic account id of the buyer. |
sender | Which of your accounts sent it. |
offer | The offer id. Remember it changes when the shop rotates. |
order_id | Your shop's own reference, if you sent one to gift_send. |
category | The gifting category that delivered it. |
from, to | A unix timestamp, or a date like 2026-08-01. |
q | Free text across receiver, item, track id and order id. |
sort, dir | date (default), price or status; desc (default) or asc. |
limit, offset | 1–200, default 50. Page with offset. |
Alongside orders[] the reply carries total and spent for the whole filter rather than the page you asked for, so a summary can stay on screen while you page through. types counts each kind under that same filter, so you can see the mix at once instead of asking once per kind.
{
"result": true, "code": "order_history",
"total": 1284, "spent": 1027200, "limit": 50, "offset": 0,
"types": { "api_gift": 900, "gift": 350, "purchase": 34 },
"orders": [
{ "id": "trk1", "date": 1755534240, "type": "api_gift", "status": "completed",
"receiver": "990e78dd\u2026", "sender": "acc1", "offer": "offer1",
"price": 800, "order_id": "1042", "category": "vip" }
]
}You only ever see your own orders. For sender IDs, original references and unknown outcomes, see gift_send reconciliation.
Linking an account #
Log into the Epic account, open this URL, and copy authorizationCode:
https://www.epicgames.com/id/api/redirect?clientId=3f69e56c7649492c8cc29f1af08a8a12&responseType=codeThen POST it to account_link with a category. Codes are single-use and expire quickly.
Code examples #
Pick a language; every endpoint below updates. Replace YOUR_KEY and the sample values.
WordPress plugin (fnitemshop-auto-gift) #
A WooCommerce plugin that calls this API on order completion: it maps a product to a Fortnite item and one of your categories, then sends the gift from that category’s accounts.
WordPress plugin — version 3.1.3
Released 2026-09-14 · WordPress 5.6+, PHP 7.4+, tested up to 6.8 · fnitemshop-auto-gift-3.1.3.zip
Download it from your API Key page — any active key can.
Release notes
3.1.3
- The leaving-the-shop badge appears on every product picture — the shop grid, the home page and anywhere else the products are shown — whichever theme or builder draws them, not only on the product page.
- Hovering the badge shows the full sentence: Leaves Fortnite's item shop in 5h 30m.
- The badge uses your site's own font, taken from the product title, on the grid and on the product page alike.
3.1.1
- Chinese (Simplified) added, making nine languages. Sites on any zh locale pick it up automatically.
- The sync button now reports what it did — added, updated, removed, and how many products carry a leaving-the-shop date.
- Each product shows how long the item is still in Fortnite's shop as a badge on its picture — 5h 30m — in the shop list and on the product page, the same chip the web shop has always shown. It turns red in the last day. On by default; there is a switch in settings.
- A gifting account whose friends list Epic reports as full is no longer asked to send friend requests. It failed identically on every order and each failure counted against the buyer. Buyers it already knows can still be gifted by it; it is tried again after a day in case friends were removed.
- The add-friend page offers the accounts a customer has already saved, the same way the product page does, with typing reserved for a new one. It also sends the Epic ID behind a saved account, so someone who renamed themselves in-game still resolves.
- New optional setting: when you add a gifting account, anyone who bought in the last 60 days and saved an account is sent a friend request from it within the hour — so the 48-hour wait is already over the next time they order, rather than starting when they arrive. Off by default: unaccepted requests occupy a limited slot on every account they went out from.
- When friend requests cannot be sent, the buyer is told which of their own Fortnite settings is in the way — requests switched off, a full friends list, or too many requests already waiting — instead of one error covering all three. Each needs a different action from them.
- A Fortnite username that does not exist is reported as such rather than as a general failure.
- Accounts Epic could not answer for are reported as "try again in a moment" instead of being counted as unable to gift. Previously a busy moment at Epic's end looked like a refusal.
- A buyer returning to the add-friend page is told how many requests are waiting for them, instead of being told requests were just sent when they had all been waiting since the last visit.
- The number of your accounts that could not send is no longer shown to the buyer. It says nothing they can act on, and one accepted account is enough to receive a gift.
- Plugin fields follow your theme's fonts and colours. Grey text and hairline borders were unreadable on dark themes.
- Tick boxes render as tick boxes on themes that restyle form controls as switches.
- Reaching the saved-account limit now says so, with a link to remove one, instead of silently deleting the oldest saved account to make room.
- Saved accounts can be managed with the
[fnitemshop_saved_accounts]shortcode, for themes that do not show the WooCommerce account form.
3.1.0
- Customers can save their Fortnite accounts on their account page and pick one at checkout instead of retyping it every order. Saved against the Epic account ID, so someone renaming themselves in-game does not create a duplicate entry or break the gift.
- New Customer accounts screen in the plugin menu: see what each customer has saved, and search by their name, email, Fortnite username, nickname or Epic ID.
- A limit on how many accounts each customer may keep, and a switch to turn saving off entirely.
- Eight languages, matching the Mini App. Regional locales such as es-ES, fa-IR and pt-BR now resolve instead of quietly falling back to English.
- Prices come from your gifting categories rather than the global V-Bucks setting.
- Gifting accounts are listed in the order they will actually be used.
- When a buyer is already a friend but has not been one for 48 hours yet, the page shows how long is left.
- Automatic updates from fnitem.shop.
3.0.0
- Moved to the v3 API.
- Products are taken off sale when the gifting service is unreachable, instead of accepting orders that cannot be delivered.
Setup #
- Link your Epic accounts and create your categories in the panel.
- Download the plugin above and install it on your WordPress site (Plugins → Add New → Upload), then activate it. Updates arrive through WordPress from then on.
- Paste your API key (from the API Key page) in the plugin settings. It binds to your site domain on first use.
- Per product: set the Fortnite item, the category to gift from, and the price.
The plugin uses the same endpoints documented under REST API — gift_check then gift_send against the mapped category. Epic requires 48 hours of friendship before a gift is allowed, so an order may complete after that wait. Sent gifts appear under Orders.