728 lines
26 KiB
YAML
728 lines
26 KiB
YAML
# Draft version. Refer to the official API documentation for more accurate information.
|
|
openapi: 3.0.0
|
|
|
|
info:
|
|
title: PluralKit
|
|
version: "1.0"
|
|
|
|
description: |
|
|
This is the API for [PluralKit](https://pluralkit.me/)! :)
|
|
|
|
# Authentication
|
|
Authentication is handled using a "system token". At the moment, the only way
|
|
to obtain a system token is to use the `pk;token` command through the Discord bot.
|
|
|
|
This will generate an opaque string you must pass as the `Authorization` header to API requests.
|
|
Many API endpoints are available anonymously, but most of them will hide information from
|
|
unauthenticated requests to align with the relevant privacy settings.
|
|
|
|
# Errors
|
|
Errors are just returned as HTTP response codes. Most error responses include a human-readable
|
|
error message as the body, but this should not be relied on. Just read the response codes :)
|
|
|
|
servers:
|
|
- url: https://api.pluralkit.me/v1
|
|
description: Primary API server (v1)
|
|
|
|
paths:
|
|
/s:
|
|
get:
|
|
summary: Returns your own system.
|
|
description: Requires authentication, and will returns the system the token belongs to.
|
|
tags: [Systems]
|
|
operationId: GetOwnSystem
|
|
security:
|
|
- TokenAuth: []
|
|
responses:
|
|
"200":
|
|
$ref: "#/components/responses/SystemResponse"
|
|
"401":
|
|
$ref: "#/components/responses/UnauthorizedError"
|
|
patch:
|
|
summary: Updates an existing system.
|
|
description: Requires authentication, and will update the system the token belongs to.
|
|
tags: [Systems]
|
|
operationId: UpdateSystem
|
|
security:
|
|
- TokenAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/System"
|
|
|
|
responses:
|
|
"200":
|
|
description: The system was updated. Returns the updated system object.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/System"
|
|
"401":
|
|
$ref: "#/components/responses/UnauthorizedError"
|
|
|
|
/s/{id}:
|
|
parameters:
|
|
- $ref: "#/components/parameters/SystemID"
|
|
get:
|
|
summary: Gets a system by its ID.
|
|
tags: [Systems]
|
|
operationId: GetSystem
|
|
description: Partial information may be returned if not authenticated with this system's token.
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/System"
|
|
"404":
|
|
description: System with the given ID not found.
|
|
|
|
/s/{id}/members:
|
|
parameters:
|
|
- $ref: "#/components/parameters/SystemID"
|
|
get:
|
|
summary: Gets a system's members.
|
|
description: |
|
|
If the API token does not belong to this system, this list may exclude any private members in the system.
|
|
tags: [Systems, Members]
|
|
operationId: GetSystemMembers
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/System"
|
|
"403":
|
|
description: The system's member list is private, and the given token either missing, invalid, or does not correspond to the system.
|
|
"404":
|
|
description: System with the given ID not found.
|
|
|
|
/s/{id}/fronters:
|
|
# TODO: rename to "latest switch" or something?
|
|
parameters:
|
|
- $ref: "#/components/parameters/SystemID"
|
|
get:
|
|
summary: Gets a system's current fronters.
|
|
tags: [Systems, Switches]
|
|
operationId: GetSystemFronters
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/FullSwitch"
|
|
"403":
|
|
description: The system's current fronters are private, and the given token is either missing, invalid, or does not correspond to the system.
|
|
"404":
|
|
description: System with the given ID was either not found, or does not have any switches registered.
|
|
|
|
/s/{id}/switches:
|
|
parameters:
|
|
- $ref: "#/components/parameters/SystemID"
|
|
get:
|
|
summary: Gets a system's switch history.
|
|
description: |
|
|
Will return the system's switch history, up to 100 entries at a time, in reverse-chronological (latest first) order.
|
|
|
|
For pagination, see the `before` query parameter.
|
|
tags: [Systems, Switches]
|
|
operationId: GetSystemSwitches
|
|
parameters:
|
|
- in: query
|
|
name: before
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
description: |
|
|
If provided, will only return switches that happened *before* (and not including) this timestamp.
|
|
|
|
This can be used for pagination by calling the endpoint again with the timestamp of the last switch of the previous response.
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
maxItems: 100
|
|
items:
|
|
$ref: "#/components/schemas/Switch"
|
|
"403":
|
|
description: The system's switch history is private, and the given token is either missing, invalid, or does not correspond to the system.
|
|
"404":
|
|
description: System with the given ID not found.
|
|
|
|
/s/switches:
|
|
post:
|
|
summary: Registers a new switch.
|
|
tags: [Switches]
|
|
operationId: RegisterSwitch
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Switch"
|
|
security:
|
|
- TokenAuth: []
|
|
responses:
|
|
"204":
|
|
description: The switch was logged.
|
|
"401":
|
|
$ref: "#/components/responses/UnauthorizedError"
|
|
|
|
/m:
|
|
post:
|
|
summary: Creates a new member in your system.
|
|
tags: [Members]
|
|
operationId: CreateMember
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Member"
|
|
security:
|
|
- TokenAuth: []
|
|
responses:
|
|
"200":
|
|
description: The member was created. Returns the newly created member object.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Member"
|
|
"401":
|
|
$ref: "#/components/responses/UnauthorizedError"
|
|
|
|
/m/{id}:
|
|
parameters:
|
|
- $ref: "#/components/parameters/MemberID"
|
|
get:
|
|
summary: Gets a member by their ID.
|
|
tags: [Members]
|
|
operationId: GetMember
|
|
security:
|
|
- {}
|
|
- TokenAuth: []
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Member"
|
|
"404":
|
|
$ref: "#/components/responses/MemberNotFoundError"
|
|
patch:
|
|
summary: Updates a member.
|
|
tags: [Members]
|
|
operationId: UpdateMember
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Member"
|
|
security:
|
|
- TokenAuth: []
|
|
responses:
|
|
"200":
|
|
description: The member was updated. Returns trhe updated member object.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Member"
|
|
"401":
|
|
$ref: "#/components/responses/MemberAuthError"
|
|
"404":
|
|
$ref: "#/components/responses/MemberNotFoundError"
|
|
|
|
delete:
|
|
summary: Deletes a member.
|
|
tags: [Members]
|
|
operationId: DeleteMember
|
|
security:
|
|
- TokenAuth: []
|
|
responses:
|
|
"200": # TODO: should be 204 (but *is* 200)
|
|
description: Member successfully deleted.
|
|
"401":
|
|
$ref: "#/components/responses/MemberAuthError"
|
|
"404":
|
|
$ref: "#/components/responses/MemberNotFoundError"
|
|
|
|
/a/{id}:
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
description: A Discord user ID.
|
|
schema:
|
|
$ref: "#/components/schemas/Snowflake"
|
|
|
|
get:
|
|
summary: Gets a system by (one of) its associated Discord accounts.
|
|
description: |
|
|
Note that it's currently not possible to get a system's registered accounts given a system ID through the API.
|
|
Consider this endpoint "one-way".
|
|
tags: [Systems, Accounts]
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/System"
|
|
"404":
|
|
description: The given user ID does not correspond to any systems.
|
|
|
|
/msg/{id}:
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
description: |
|
|
A Discord message ID.
|
|
|
|
This may refer to either the original "trigger message" posted by the user,
|
|
or to the resulting webhook message posted by PluralKit.
|
|
The former may be useful for eg. logging bot integration.
|
|
schema:
|
|
$ref: "#/components/schemas/Snowflake"
|
|
|
|
get:
|
|
summary: Gets information about a proxied message by its message ID.
|
|
tags: [Proxying]
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Message"
|
|
"404":
|
|
description: The given message ID does not correspond to a message proxied by PluralKit.
|
|
|
|
components:
|
|
responses:
|
|
UnauthorizedError:
|
|
description: System token is missing or invalid.
|
|
|
|
NotOwnSystemError:
|
|
description: The given system does not correspond with this token.
|
|
|
|
MemberAuthError:
|
|
# TODO (relevant, ish): This is always returned as 401 but should be split into "invalid token" (401) and "not own member" (403) responses
|
|
description: System token is missing, invalid, or the corresponding system does not own the given member.
|
|
|
|
MemberNotFoundError:
|
|
description: A member by the given ID was not found.
|
|
|
|
SystemResponse:
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/System"
|
|
|
|
MemberListResponse:
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Member"
|
|
|
|
parameters:
|
|
SystemID:
|
|
in: path
|
|
name: id
|
|
required: true
|
|
description: The ID of the system in question.
|
|
schema:
|
|
$ref: "#/components/schemas/ID"
|
|
|
|
MemberID:
|
|
in: path
|
|
name: id
|
|
required: true
|
|
description: The ID of the member in question.
|
|
schema:
|
|
$ref: "#/components/schemas/ID"
|
|
|
|
schemas:
|
|
ID:
|
|
type: string
|
|
readOnly: true
|
|
minLength: 5
|
|
maxLength: 5
|
|
pattern: "^[a-z]{5}$"
|
|
example: "abcde"
|
|
description: |
|
|
A unique identifier for a system or a member, as a randomly generated string of five lowercase letters.
|
|
|
|
These IDs are guaranteed to be globally unique for the given model type (a system can have the same ID as a member, but two systems or two members can never share an ID).
|
|
|
|
The IDs can on rare occasions change - eg. if a profane word is generated and later regenerated, or as a potential future Patreon perk. However, it's still reasonable to assume that the IDs are constant, and that tey won't change without the user's knowledge, so it's safe to store in things like settings menus and config files.
|
|
System:
|
|
properties:
|
|
id:
|
|
$ref: "#/components/schemas/ID"
|
|
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
maxLength: 100
|
|
description: The user-provided name of the system.
|
|
example: "Boxes of Foxes"
|
|
|
|
description:
|
|
type: string
|
|
nullable: true
|
|
maxLength: 1000
|
|
description: |
|
|
The user-provided system description.
|
|
|
|
May contain rich text in Markdown, including standard Markdown-formatted links, or Discord-formatted emoji/user/channel references.
|
|
example: |-
|
|
This system is very cool.
|
|
|
|
It has cool people.
|
|
tag:
|
|
type: string
|
|
maxLength: 78
|
|
description: |
|
|
The system tag, which is appended to the names (or display names, if set) of members when proxying through the bot.
|
|
|
|
A space will automatically be inserted between the name and the tag, so no need to include one at the start.
|
|
|
|
The character limit here is 78, as Discord's name length limit for webhooks is 80 characters. A 78-character system tag is thus the longest tag that can still accomodate a single-letter member name and the separating space.
|
|
nullable: true
|
|
example: "| BoF"
|
|
|
|
avatar_url:
|
|
type: string
|
|
format: url
|
|
nullable: true
|
|
maxLength: 256
|
|
example: "https://i.imgur.com/Abcdefg.png"
|
|
description: |
|
|
A link to the avatar/icon of the system.
|
|
|
|
If used for proxying, the image must be at most 1000 pixels in width *and* height, and at most 1 MiB in size. This restriction is on Discord's end and is not verified through the API (it's simply stored as a string).
|
|
|
|
tz:
|
|
type: string
|
|
format: timezone
|
|
nullable: true
|
|
default: UTC
|
|
example: America/New_York
|
|
description: |
|
|
The system's registered time zone as an Olson time zone ID.
|
|
|
|
This is used in the bot to convert various time stamps in commands on behalf of this system.
|
|
|
|
created:
|
|
type: string
|
|
format: date-time
|
|
readOnly: true
|
|
description: The creation timestamp of the system.
|
|
|
|
description_privacy:
|
|
allOf:
|
|
- $ref: "#/components/schemas/PrivacySetting"
|
|
- description: |
|
|
The system's description privacy setting, either "public" or "private".
|
|
|
|
If this is set to "private", the field `description` will be returned as `null` on all requests not authorized with this system's token.
|
|
|
|
In addition, this field will be returned as `null` if the request is not authorized with this system's token.
|
|
|
|
Because of this, there is no way for an unauthorized user to tell the difference between a private description and a `null` description - this is intentional.
|
|
|
|
member_list_privacy:
|
|
allOf:
|
|
- $ref: "#/components/schemas/PrivacySetting"
|
|
- description: |
|
|
The system's member list privacy setting, either "public" or "private".
|
|
|
|
If this is set to "private", this system's member list can not be queried without proper authorization.
|
|
|
|
In addition, this field will be returned as `null` if the request is not authorized with this system's token.
|
|
|
|
front_privacy:
|
|
allOf:
|
|
- $ref: "#/components/schemas/PrivacySetting"
|
|
- description: |
|
|
The system's current fronter privacy setting, either "public" or "private".
|
|
|
|
If this is set to "private", this system's current fronter can not be queried without proper authorization.
|
|
|
|
In addition, this field will be returned as `null` if the request is not authorized with this system's token.
|
|
|
|
front_history_privacy:
|
|
allOf:
|
|
- $ref: "#/components/schemas/PrivacySetting"
|
|
- description: |
|
|
The system's front history privacy setting, either "public" or "private".
|
|
|
|
If this is set to "private", this system's front/switch history can not be queried without proper authorization.
|
|
|
|
In addition, this field will be returned as `null` if the request is not authorized with this system's token.
|
|
|
|
Member:
|
|
properties:
|
|
id:
|
|
$ref: "#/components/schemas/ID"
|
|
|
|
name:
|
|
type: string
|
|
maxLength: 100
|
|
description: The user-provided name of the member.
|
|
example: "Myriad Kit"
|
|
|
|
display_name:
|
|
type: string
|
|
maxLength: 100
|
|
description: The member's "display name", which will override the member's normal name when proxying.
|
|
nullable: true
|
|
example: Myriad 'Big Boss' Kit
|
|
|
|
description:
|
|
type: string
|
|
maxLength: 1000
|
|
description: |
|
|
The user-provided description of the member.
|
|
|
|
May contain rich text in Markdown, including standard Markdown-formatted links, or Discord-formatted emoji/user/channel references.
|
|
|
|
If this member is private, and the request is not authorized with the member's system token, this field will always be returned as `null`.
|
|
|
|
example: Myriad is very cool and rad, and they love snuggling.
|
|
|
|
color:
|
|
type: string
|
|
format: color
|
|
minLength: 6
|
|
maxLength: 6
|
|
nullable: true
|
|
pattern: "^[0-9A-Fa-f]{6}$"
|
|
description: |
|
|
The member's "color", displayed on member cards, as a 6-character hexadecimal color code (no leading #).
|
|
|
|
If this member is private, and the request is not authorized with the member's system token, this field will always be returned as `null`.
|
|
|
|
example: "FF0000"
|
|
|
|
birthday:
|
|
type: string
|
|
format: date
|
|
example: "2018-07-11"
|
|
nullable: true
|
|
description: |
|
|
The user-provided birthdate of the member.
|
|
|
|
"Year-less" birthdays are supported. In this case, the year should be set to `0004`, and that specific year should be special-cased and hidden from the user. Previous versions used the year `0001` for the same purpose, and this value may still be both read and written with the API and should be treated the same as `0004`.
|
|
|
|
The year `0004` was chosen because it is a leap year in the Gregorian calendar, and thus the date `0004-02-29` can be properly represented.
|
|
|
|
If this member is private, and the request is not authorized with the member's system token, this field will always be returned as `null`.
|
|
|
|
pronouns:
|
|
type: string
|
|
maxLength: 100
|
|
example: "they/them or xe/xem"
|
|
nullable: true
|
|
description: |
|
|
The user-provided pronouns of the member.
|
|
|
|
There is no specific schema, just a freeform text field.
|
|
|
|
If this member is private, and the request is not authorized with the member's system token, this field will always be returned as `null`.
|
|
|
|
avatar_url:
|
|
type: string
|
|
format: url
|
|
nullable: true
|
|
maxLength: 256
|
|
example: "https://i.imgur.com/Abcdefg.png"
|
|
description: |
|
|
A link to the avatar/icon of the member.
|
|
|
|
If used for proxying, the image must be at most 1000 pixels in width *and* height, and at most 1 MiB in size. This restriction is on Discord's end and is not verified through the API (it's simply stored as a string).
|
|
privacy:
|
|
allOf:
|
|
- $ref: "#/components/schemas/PrivacySetting"
|
|
- description: |
|
|
The member's privacy setting, either "public" or "private".
|
|
|
|
If this is set to "private", the member will not appear in public member lists. It can still be looked up using its 5-character member ID, but will return limited information.
|
|
|
|
Specifically, the properties `birthday`, `pronouns` and `description` will always be returned as `null` if a valid token for the system isn't provided, even if the underlying value is present.
|
|
|
|
In addition, this field will be returned as `null` if the request is not authorized with this system's token.
|
|
|
|
proxy_tags:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/ProxyTag"
|
|
description: |
|
|
An unordered list of the member's proxy tag pairs.
|
|
|
|
It is valid for a member to have any number of proxy tags, including none at all.
|
|
|
|
prefix:
|
|
type: string
|
|
nullable: true
|
|
description: |
|
|
Previous versions of the API only supported a single proxy tag pair per member.
|
|
This field will contain the prefix of the first proxy tag registered, or `null` if missing.
|
|
Setting it will write to the first proxy tag's prefix, creating it if not present.
|
|
|
|
This field is deprecated and will be removed in API v2.
|
|
deprecated: true
|
|
|
|
suffix:
|
|
type: string
|
|
nullable: true
|
|
description: |
|
|
Previous versions of the API only supported a single proxy tag pair per member.
|
|
This field will contain the suffix of the first proxy tag registered, or `null` if missing.
|
|
Setting it will write to the first proxy tag's suffix, creating it if not present.
|
|
|
|
This field is deprecated and will be removed in API v2.
|
|
deprecated: true
|
|
|
|
keep_proxy:
|
|
type: boolean
|
|
default: false
|
|
description: |
|
|
Whether or not to include the used proxy tags in proxied messages.
|
|
|
|
PrivacySetting:
|
|
type: string
|
|
nullable: true
|
|
description: A privacy setting for systems or members, either public or private. May occasionally be `null` in cases where you are not authorized to view the privacy setting's state.
|
|
enum: [public, private]
|
|
example: public
|
|
|
|
ProxyTag:
|
|
description: |
|
|
Represents a set of proxy tags to match messages on.
|
|
|
|
A "proxy tag" consists of a prefix and a suffix, and a given proxy tag set matches a string
|
|
if that string begins with the prefix and ends with the suffix. They're often represented to the user with the word `text` between them, eg. `[text]`, `{{text`, etc.
|
|
|
|
For example, the proxy tag pair "[" and "]" will match any string [in square brackets].
|
|
|
|
Either the prefix or the suffix may be missing (or both may be present), but it is invalid for
|
|
both values to be null.
|
|
properties:
|
|
prefix:
|
|
type: string
|
|
nullable: true
|
|
description: |
|
|
The proxy tag prefix. This is the string that goes *before* the inner text.
|
|
|
|
An empty prefix is represented as `null` and an empty string will be converted as such.
|
|
example: "{{"
|
|
maxLength: 100
|
|
suffix:
|
|
type: string
|
|
nullable: true
|
|
description: |
|
|
The proxy tag suffix. This is the string that goes *after* the inner text.
|
|
|
|
An empty suffix is represented as `null` and an empty string will be converted as such.
|
|
example: "}}"
|
|
maxLength: 100
|
|
|
|
Switch:
|
|
properties:
|
|
timestamp:
|
|
type: string
|
|
format: date-time
|
|
description: The timestamp the switch was logged.
|
|
readOnly: true
|
|
members:
|
|
type: array
|
|
description: |
|
|
A list of the IDs of the members in this switch. The order is significant. It is valid for the switch to have no members at all.
|
|
items:
|
|
$ref: "#/components/schemas/ID"
|
|
|
|
FullSwitch:
|
|
description: A variant of the Switch schema where the full Member object is contained instead of just IDs.
|
|
properties:
|
|
timestamp:
|
|
type: string
|
|
format: date-time
|
|
description: The timestamp the switch was logged.
|
|
members:
|
|
type: array
|
|
description: |
|
|
A list of the members in this switch. The order is significant. It is valid for the switch to have no members at all.
|
|
items:
|
|
$ref: "#/components/schemas/Member"
|
|
|
|
Snowflake:
|
|
description: |
|
|
A unique identifier used by Discord for its objects (accounts, guilds, channels, messages).
|
|
|
|
This is internally stored as a 64-bit unsigned integer, but is represented as a string in the API
|
|
to accomodate languages that store numbers as floating point values (and thus would not be able to represent it losslessly).
|
|
type: string
|
|
pattern: "^[0-9]{17,19}"
|
|
minLength: 17
|
|
maxLength: 19
|
|
example: 466378653216014359 # PK's account ID :3
|
|
|
|
Message:
|
|
description: |
|
|
An object containing information about a proxied message, including message, user and channel IDs, as well as the system and member it's related to.
|
|
|
|
For privacy and performance reasons, this endpoint does not return the *contents* of the original message. This data isn't stored in the database either way - but given the channel and message ID, it can be fetched from Discord's own API.
|
|
properties:
|
|
timestamp:
|
|
type: string
|
|
format: date-time
|
|
readOnly: true
|
|
description: The time the message was proxied.
|
|
id:
|
|
allOf:
|
|
- $ref: "#/components/schemas/Snowflake"
|
|
- description: "The ID of the proxied webhook message posted by PluralKit."
|
|
original:
|
|
allOf:
|
|
- $ref: "#/components/schemas/Snowflake"
|
|
- description: "The ID of the original (now-deleted) trigger message containing the proxy tags."
|
|
sender:
|
|
allOf:
|
|
- $ref: "#/components/schemas/Snowflake"
|
|
- description: "The ID of the Discord user that sent the trigger message."
|
|
channel:
|
|
allOf:
|
|
- $ref: "#/components/schemas/Snowflake"
|
|
- description: "The ID of the Discord channel the relevant messages were posted in."
|
|
system:
|
|
allOf:
|
|
- $ref: "#/components/schemas/System"
|
|
- description: "The system that proxied the message."
|
|
member:
|
|
allOf:
|
|
- $ref: "#/components/schemas/Member"
|
|
- description: "The member that proxied the message."
|
|
|
|
securitySchemes:
|
|
TokenAuth:
|
|
type: apiKey
|
|
in: header
|
|
name: Authorization |