CEEJAYDocs

Deduct XP

Remove XP from a member. Never drops below zero.

POST /v1/xp/deduct · requires xp:deduct

Body parameters

Prop

Type

Returns

Prop

Type

The response also includes the original field names. They contain the same values, and remain for as long as version 1 exists.

Deprecated field, still sentSame value as
totalXPxp
previousXPpreviousXp
currentLevellevel

Example

Request
curl -X POST https://ceejay.communitystudios.xyz/api/v1/xp/deduct \
  -H "Authorization: Bearer $CEEJAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "discordId": "409876543210987654",
    "amount": 200,
    "reason": "Reversed a duplicate award",
    "idempotencyKey": "reversal-10442"
  }'

The TypeScript example uses ceejay.xp.deduct from the TypeScript SDK. It returns the data of the response below.

200 OK
{
  "success": true,
  "data": {
    "discordId": "409876543210987654",
    "previousXP": 200,
    "xpDeducted": 200,
    "totalXP": 0,
    "previousLevel": 1,
    "currentLevel": 0,
    "reason": "Reversed a duplicate award",
    "auditId": "10443",
    "xp": 0,
    "previousXp": 200,
    "level": 0,
    "duplicate": false
  }
}

Behaviour

Deducting more than the member has sets their total to zero rather than returning an error. xpDeducted reports what was actually removed, which may be less than amount.

A member CEEJAY has no profile for returns 404 with the code member_not_found. Unlike Grant XP, this request does not create a profile, and it does not check with Discord.

When something was removed, the change goes into the member's XP history as a negative entry. The XP, its history entry and the idempotency key are committed in a single transaction. A deduction does not change the member's season XP.

Retrying safely

Send an idempotencyKey with every call you might need to retry, such as after a timeout. It is a string of 1 to 128 characters that you choose, sent in the JSON body: an Idempotency-Key header is not read.

  • Retrying with the same key returns the original response. When the same request is sent again with its key, CEEJAY returns the original response, with duplicate: true and the header Idempotent-Replayed: true.
  • Each key is tied to one request payload. The same request means the same member, amount and reason. Two requests for one event need two keys, for example match-9:coins and match-9:xp. A key that was used for a different request, here or on any other request of the API, returns 409 with the code idempotency_key_reused, and nothing is changed.
  • A rejected request does not use up the key. A call that was rejected can be retried with the same key once the cause is fixed.
  • A key belongs to the API key that sent it. Another API key of your community can send the same text, and it is a different key.
  • Keys are retained for 30 days. Use a value you can regenerate after a crash, such as an order ID or a match ID.
  • On an instance that does not support idempotency keys yet, a call that sends one returns 503 with the code idempotency_unavailable, and nothing is changed. Community details reports whether keys are supported, under features.retryKeys. idempotency_key is not accepted on this endpoint; use idempotencyKey.
  • Without a key, a retried call is a new call.

Errors

StatuscodeerrorWhen
400bad_requestThe request body must be a JSON objectThe body is null, text, a number or a list. A request with no Content-Type header is read as text, so it gets this error too
400bad_requestdiscordId must be a valid Discord snowflake (17-20 digits)discordId is missing or is not a string of 17 to 20 digits. A JSON number is rejected
400bad_requestamount must be a positive integeramount is missing, zero, negative, fractional or sent as text
400bad_requestamount cannot exceed 2147483647More than 2,147,483,647 in one call
400bad_requestreason cannot contain a null characterreason contains a null character (U+0000), which cannot be stored
400bad_requestreason must be a stringreason was sent and is not a string: a number, a list, an object, true or false. null counts as left out
400bad_requestidempotencyKey must be a string of 1-128 charactersThe key is empty, longer than 128 characters or not text, or contains an unpaired surrogate, such as the JSON "\ud800"
400bad_requestidempotencyKey cannot contain a null characterThe key contains a null character (U+0000)
404member_not_foundMember not foundCEEJAY has no profile for this ID on your server. Nothing was deducted
409idempotency_key_reusedThis idempotencyKey was already used for a different requestYour API key already used this idempotencyKey for a different request: a request of another kind, or this one with other values. Nothing was changed
409idempotency_in_progressThe first request with this idempotencyKey is still running. Try again in a few seconds.The first request with this key is still being processed. Wait retryAfter seconds and retry
503idempotency_unavailableidempotencyKey cannot be used on this community yet, so nothing was changedYou sent a key, and this instance does not support idempotency keys yet. Nothing was changed. Community details reports it as features.retryKeys

Every error body also includes requestId, and a 400 with the code bad_request identifies the invalid field in field. Match on code; message text can change.

The errors every request shares (401, 403, 405, 429, 500) are on the Errors page.

On this page

Community StudiosPrivacyTerms