Utviklere

Koble dine egne systemer til Oase: legg drakter i garderoben, legg dem ut for salg eller ta dem av igjen, og følg statusen og hva de ble solgt for.

Utviklerdokumentasjonen er på engelsk.

Oase Public API

The Oase Public API lets your own systems manage your shirts on Oase: add shirts to your locker, put them up for sale or take them off again, and follow where each one stands and what your sales went for.

It works for private sellers and for shops. A key always belongs to one account, either you or a shop you're an admin of, and only ever works with that account's shirts and sales. Shirts created with a shop's key belong to the shop; Oase records them as added by the admin who created the key.

Getting started

  1. Create a key. For your own account, go to Account settings. For a shop, switch to the shop and open its Shop page (shop admins only). You'll also find both under Settings → Developers in the account menu. Give the key a name you'll recognise, such as "Inventory sync".
  2. Copy it straight away. The key is shown once. If you lose it, revoke it and create a new one.
  3. Make a request:
curl https://public-api.oase.ai/v1/me \
  -H "Authorization: Bearer oase_pk_..."
{ "type": "shop", "name": "Test Shop" }

Authentication

Send the key on every request, in either header:

Authorization: Bearer oase_pk_...
X-API-Key: oase_pk_...

Keep keys on your server. Never put one in a website, an app or a public repository: anyone who has it can read your shirts and sales, and list or unlist your shirts. If a key may have leaked, revoke it in Oase; it stops working immediately.

Base URL: https://public-api.oase.ai

Endpoints

MethodPathDoes
GET/v1/meWho the key belongs to
GET/v1/itemsYour shirts, newest first
GET/v1/items/:idOne of your shirts
POST/v1/imagesUploads a photo for a new shirt
GET/v1/clubs?search=Finds clubs
GET/v1/players?search=Finds players
GET/v1/seasonsLists seasons
POST/v1/itemsAdds a shirt to your locker
POST/v1/items/:id/listingPuts a shirt up for sale
POST/v1/items/:id/unlistTakes a shirt off the market
GET/v1/salesYour paid sales, newest first
GET/v1/sales/:idOne of your sales

Items

GET /v1/items lists the shirts you own, each with where it stands and its listing, if it has one. A shirt that sells moves to its buyer, so it leaves this list and shows up under sales instead.

{
  "data": [
    {
      "id": "cmgx1b2c30001",
      "title": "Erik Huseklepp Brann 2007/08 home",
      "club": "Brann",
      "season": "2007/08",
      "player": "Erik Huseklepp",
      "kitType": "home",
      "size": "l",
      "condition": 9,
      "verified": true,
      "createdAt": "2026-10-04T15:08:04.701Z",
      "status": "in_auction",
      "listingType": "auction",
      "price": 500,
      "currentBid": 650,
      "bidCount": 2,
      "currency": "NOK",
      "listedAt": null,
      "auctionStartsAt": "2026-10-08T15:00:00.000Z",
      "auctionEndsAt": "2026-10-09T15:00:00.000Z"
    }
  ],
  "nextCursor": null
}

price is the fixed price, or an auction's start price. currentBid and bidCount are only set for auctions; listedAt only for fixed price.

statusMeaning
in_lockerNot for sale (also an auction that ended without bids)
waiting_for_verificationListed, waiting for Oase to check the shirt
waiting_for_approvalOase adjusted the listing; waiting for you to accept
for_saleFixed price, can be bought now
auction_scheduledAuction set up, not started yet
in_auctionAuction running
awaiting_paymentSold, waiting for the buyer's payment

Adding a shirt

A new shirt needs at least two photos and the ids of its club and season (and player, if it has a name on the back). It goes into your locker, not for sale yet.

1. Upload the photos, one per request, front first. Send the file as multipart/form-data in a field named file, or as the raw body with its Content-Type. JPEG, PNG or WebP, at most 10 MB and 40 megapixels, at least 400 pixels on each side. Oase turns photos upright (phone photos often arrive sideways), scales them down and answers with the photo's URL:

curl -X POST https://public-api.oase.ai/v1/images \
  -H "Authorization: Bearer oase_pk_..." \
  -F "file=@front.jpg"
{ "url": "https://oasepbassets.blob.core.windows.net/images/0b6f...-api__thumbed.jpg" }

2. Look up the club, season and player. Each search returns up to 25 matches, best first, as { "data": [{ "id": "...", "name": "Brann" }] }. search needs at least two letters for clubs and players; seasons can be listed without it.

curl "https://public-api.oase.ai/v1/clubs?search=brann" \
  -H "Authorization: Bearer oase_pk_..."

The API only uses clubs, players and seasons Oase already knows. If one is missing, add the shirt in the app, or leave the player out and mention them in the description.

3. Create the shirt:

curl -X POST https://public-api.oase.ai/v1/items \
  -H "Authorization: Bearer oase_pk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "clubId": "cmgx0brann001",
    "seasonId": "cmgx0season0708",
    "playerId": "cmgx0huseklepp",
    "kitType": "home",
    "size": "l",
    "condition": 9,
    "description": "Match worn, signed on the back",
    "images": ["https://oasepbassets.blob.core.windows.net/images/0b6f...-api__thumbed.jpg", "..."]
  }'
FieldNotes
clubId, seasonIdRequired, from the searches above
playerIdOptional
kitTypehome, away or third
sizexs, s, m, l, xl, xxl or xxxl
condition1 (worn) to 10 (as new)
descriptionOptional, up to 2000 characters
images2 to 12 URLs from POST /v1/images, front first

The answer is the new shirt (201), in the same shape as GET /v1/items/:id, with status in_locker. Fields Oase doesn't know are rejected rather than ignored, so a typo shows up as a 400.

Putting a shirt up for sale

POST /v1/items/:id/listing lists a shirt from your locker at a fixed price or as an auction. Prices are whole kroner (or your currency's whole units).

curl -X POST https://public-api.oase.ai/v1/items/cmgx1b2c30001/listing \
  -H "Authorization: Bearer oase_pk_..." \
  -H "Content-Type: application/json" \
  -d '{ "type": "fixed_price", "price": 1500 }'

An auction takes its start price, end and, optionally, when it starts:

{
  "type": "auction",
  "startPrice": 500,
  "bidIncrement": 50,
  "startsAt": "2026-10-20T18:00:00+02:00",
  "endsAt": "2026-10-27T20:00:00+01:00",
  "antiSnipeSeconds": 300
}
FieldNotes
typefixed_price or auction
priceFixed price only, required
startPriceAuction only, required: the opening bid
bidIncrementAuction only: the smallest raise between bids (default 50)
startsAtAuction only: when it starts, at most 60 days ahead. Leave it out to start now
endsAtAuction only, required: 1 hour to 30 days after the start
antiSnipeSecondsAuction only: a bid this close to the end extends the auction by this much, 30-600 (default 300)

Dates are ISO 8601 with a time zone, such as 2026-10-20T18:00:00Z or 2026-10-20T20:00:00+02:00.

The answer is the shirt (201). Oase checks every shirt before it's sold the first time: until then its status is waiting_for_verification, and it goes on sale by itself once approved. Shirts Oase has already verified go on sale straight away.

To sell, your account needs the same as in the app: payouts set up, your country, a phone number and a sender address for shipping. If something is missing you get 403 with cannot_sell and a message saying what to add. A shirt can only have one listing at a time: listing one that's already listed gives 409 with already_listed.

Taking a shirt off the market

POST /v1/items/:id/unlist ends the listing or auction and puts the shirt back in your locker, as long as nobody has a claim on it yet. The answer is the shirt, back to in_locker. Otherwise you get 409 with:

errorWhy
has_bidsThe auction has bids
checkout_in_progressA buyer is paying, or has paid
not_listedThe shirt isn't listed

Sales

GET /v1/sales lists your paid sales, with what the buyer paid and how the parcel is getting on.

{
  "data": [
    {
      "id": "cmgx9z8y70004",
      "itemId": "cmgx1b2c30002",
      "title": "Brann 2007/08 home",
      "club": "Brann",
      "season": "2007/08",
      "player": null,
      "kitType": "home",
      "saleType": "fixed_price",
      "price": 1500,
      "currency": "NOK",
      "soldAt": "2026-10-02T15:08:04.703Z",
      "shippingStatus": "delivered"
    }
  ],
  "nextCursor": null
}
shippingStatusMeaning
awaiting_shipmentPaid; no shipping code yet
bookedShipping code ready; not handed in yet
in_transitBring has the parcel
deliveredDelivered or collected

Prices are in whole units of currency (kroner for NOK). Dates are ISO 8601 in UTC.

Paging

Lists return up to 50 results by default. Ask for between 1 and 100 with limit. When there is more, nextCursor is set: pass it as cursor to get the next page. It is null on the last page.

curl "https://public-api.oase.ai/v1/items?limit=100&cursor=cmgx1b2c30001" \
  -H "Authorization: Bearer oase_pk_..."

Rate limits

Each key can make 60 requests a minute, reads and writes together. Every response tells you where you stand:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1791471600

Over the limit you get 429 with a Retry-After header (seconds). Reset is a Unix timestamp.

Errors

Errors are JSON with a stable error code and a readable message:

{ "error": "invalid_api_key", "message": "Send a valid API key as `Authorization: Bearer <key>`." }
StatuserrorWhen
400invalid_requestA parameter or field is invalid; the message names it
400invalid_photoThe photo isn't JPEG, PNG or WebP, or is too large or too small
401invalid_api_keyMissing, unknown or revoked key
403cannot_sellYour account isn't ready to sell yet; the message says what to add
403account_blockedThe account behind the key can't add shirts
404not_foundNo such endpoint, or not one of your shirts or sales
409already_listed, has_bids, checkout_in_progress, not_listedThe shirt can't be listed or unlisted right now
429rate_limitedToo many requests; wait for Retry-After
500internal_errorSomething went wrong on our side
503uploads_unavailablePhotos can't be stored right now; try again

Request log

To keep Oase safe and to help when something goes wrong, Oase keeps a log of the requests made with your keys for 90 days: when, which endpoint, the result, the IP address and the user agent. It never stores what you sent or what the API answered.

What the API never returns

To keep buyers safe if a key leaks, the API never includes anything about your buyers: no names, addresses or contact details, and no tracking numbers (a parcel's tracking page shows where it is going). Responses also leave out photos.

Reference

The full, interactive reference with every field is at public-api.oase.ai/docs.

Questions or ideas? Write to hei@oase.ai.