Unless otherwise stated, request fields are (probably) optional.
Also, pretend that the JSON is valid :) I left out some double quotes to make it a bit more cleaner.
In general, APIs will respond in the following format:
{
status: <status code>,
message: <main payload>
}
/api/get/:objectType
Get the result of a search query for any object type. Queries are checked against readCheck
policy.
objectType
refers to the name of the object being operated on (duh).
The API requires the object type passed in as a query parameter. Additionally, a search query must be specified in the body of the request.
{
page: 1, // Required - 1 indexed page number to fetch
size: 20, // Required - Number of objects per page
sortCriteria: "asc", // Can be asc or desc
sortField: "profile.internal.applicationScore", // Path to field to use for sort
text: "Banana", // Is added as an "OR" filter
filter: { // Any additional manual filters
"$or": [ // If any of these expressions are true, then there's a match
{ baz: "bar" },
{ bang: "pang" },
],
"$and": [ // All these expressions need to be true
{ lastName: "foo" },
{ firstName: "baz", bop: "beep" },
],
beep: "boop", // Statements like this on the top level also need to be true
internal: {
notes: "trash" // Nested field (note this is different than the boo.bap example as internal must match this map exactly)
},
"boo.bap": "reee"
}
}
The API will return an array of maps corresponding to the queried object. Note that the number of items returned may not match the limit specified in the query if some objects were filtered out as part of field sanitation.
{
status: 200,
message: [
{
...results1
},
{
...results2
}
]
}
/api/edit/:objectType
Edit existing objects. Changes are validated against writeCheck
policy.
WARNING! This is an EXTREMELY dangerous operation! If the query is incorrect, it could potentially break the database.
Remember that {}
means match EVERYTHING.
objectType
refers to the name of the object being operated on (duh).
{
filter: { // All objects that match this filter (mongoDB format) will be affected
_id: 12345
},
changes: { // Changes to apply to the object (mongoDB format)
firstName: "foobar"
},
noFlatten: false // if true, the changes will REPLACE rather than merge
}
{
status: 200,
message: [ // IDs of objects that were changed
"id1234",
"id1235",
...
]
}
/api/delete/:objectType
Delete existing objects. Changes are validated against deleteCheck
policy.
WARNING! This is an EXTREMELY dangerous operation! If the query is incorrect, it could potentially nuke the database.
Remember that {}
means match EVERYTHING.
objectType
refers to the name of the object being operated on (duh).
All objects that match the filter query will be deleted.
{ // The entire body is the mongoDB filter query
_id: 12345,
firstName: "foobar",
...
}
WARNING! This is an EXTREMELY dangerous operation! If the query is incorrect, it could potentially nuke the database.
Remember that {}
means match EVERYTHING.
{
status: 200,
message: [ // IDs of objects that were deleted
"id1234",
"id1235",
...
]
}
/api/create/:objectType
Create new object. Requests are validated against createCheck
and writeCheck
policy.
objectType
refers to the name of the object being operated on (duh).
Note that all inputs will be validated using writeCheck
.
{ // Initial values for the new object
firstName: "test",
lastName: "bar",
....
}
{
status: 200,
message: "id1234" // ID of the new object is returned
}
/api/action/initializeSettingsMapper
Iterates through all documents and ensures the settingsMapper field is populated. We use this field to populate the user object with data from global settings, such as deadlines. Refer to the README for more information.
This endpoint should only be needed for migrating old databases.
{
status: 200,
message: "ok"
}
/api/gridfs?filename=<filename goes here>
Fetch a file stored in GridFS given its name. Note that it isn't guaranteed for files to have
unique names if they are added to GridFS through methods other than writeGridFSFile
.
Specify the name of the file to be fetched as a query parameter.
The queried file will be returned if successful.
/api/gridfs?filename=<filename goes here>
Upload a file to GridFS with a specific file name. If a file exists with that name, delete it first.
Specify the filename as a query parameter and submit the file to be uploaded with the name "file".
For example, a file can be manually added by running:
http --form put localhost:3005/api/gridfs?filename=thisisthefilename.pdf [email protected] x-access-token:$TOKEN
{
"message": "Success",
"status": 200
}
/api/gridfs?filename=<filename goes here>
Delete a file from GridFS given its name if it exists.
Specify the file to be deleted as a query parameter.
{
"message": "Success",
"status": 200
}
Error 404 will be returned if the file is not found.
/api/action/profile
Get the sanitized user object corresponding to the requesting user.
{
status: 200,
message: {
// User object goes here
}
}
/api/action/updateapp
Update the hacker application
{
submit: true, // Whether to mark the application for submission
application: {
// Application content goes here
}
}
{
status: 200,
message: "Success"
}
On failure:
{
status: 403,
message: ["firstName", "lastName"] // Return an array of fields that failed validation
}
/api/action/updateresume
Send the resume file with the name field resume
and magic will happen!
/api/action/applicationEnums
Dictionary of fields with enums and an array of valid values.
/api/action/createTeam
Create a new team and add the user to it.
{
status: 200,
message: {
code: "teamcode goes here",
memberNames: [
"Bill Gates"
]
}
}
/api/action/joinTeam
Join an existing team if there is room.
{
teamCode: "teamcodegoeshere"
}
{
status: 200,
message: {
code: "teamcodegoeshere",
memberNames: [
"Bill Gates"
]
}
}
/api/action/leaveTeam
Remove the user from their team, and delete it if they were the last user.
{
status: 200,
message: "Success"
}
/api/action/getTeam
Get the user's current team
{
status: 200,
message: {
code: "teamcodegoeshere",
memberNames: [
"Bill Gates"
]
}
}
/api/action/applicationSettings
Returns the global settings object
{
status: 200,
message: {
...
}
}
/api/action/discordOAuthUrl
Returns a URL to begin the Discord OAuth process.
{
redirectUrl: "<string>"
}
{
status: 200,
message: "<url>"
}
/api/action/discordOAuthUrl
Finishes the Discord OAuth process and associates the user, given a state and code.
{
state: "<string>",
code: "<code>"
}
{
status: 200,
message: "OK"
}
/api/action/getStatistics?update=false
Change update
in the query string to true
if the most up to date statistics are required immediately.
This will also update the cache, which by default has a lifetime of 5 minutes.
{
status: 200,
message: {
...stats go here, look at src/services/statistics.ts for the full typedef
}
}
/api/action/syncMailingLists
Trigger a mailing list sync with Mailtrain. If forceUpdate
is enabled, all users
eligible for a mailing list will be sent to mailtrain, even if they are already synced, to ensure all tags
are updated too.
{
mailingList: <array of name of mailing list to sync> (all if omitted),
forceUpdate: true | false,
email: <if specified, only changes will be made to this address>
}
{
status: 200,
message: {
added: [
... emails that were sent to mailtrain to be added/updated
],
deleted: [
... emails removed from mailing list
]
}
}
/api/action/verifyMailingList
This will add a user to every registered mailing list with the (expected) name of the mailing list in their email.
This can be used to verify that the correct list ID was used; however, it does NOT verify that the query is correct.
The request user's mail merge will be used for all of the test users.
{
status: 200,
message: [
// list of the names of mailing lists that were verified
]
}
/api/action/sendEmail
Send an email to a user using the Mailtrain transactional API. If email
corresponds to a user registered
in the system, tags will automatically be injected with information such as application deadline, confirmation deadline, etc.
{
email: <email of the recipient>
templateName: <name of the template to send>
tags: <dictionary of tags to inject for mailmerge>
}
{
status: 200,
message: "Success"
}
/api/action/templateTest
Send the requester an instance of every available email template to ensure everything looks correct. This endpoint should be called after configuring the server to ensure that the correct template IDs were used, and that all the mail merges were successful.
In the email, each mail merge field should be replaced by something that looks like <<<FIELD goes here>>>
,
where FIELD
is the name of the field it is replacing. Ensure that FIELD
matches what you expect should be filled
in that spot.
{
status: 200,
message: [
// List of template names that were sent
]
}
/api/action/releaseApplicationStatus
Set application released status to true for all users who have been either waitlisted, accepted, or rejected
{
status: 200,
message: [
// List of users who were updated
]
}
/api/action/getRanks?usePersonalScore=<true | false>
Since application scores are a computed value, we cannot sort them in our query. Instead, we will have to use this dedicated endpoint to have it sorted manually.
usePersonalScore
can be optionally passed in the query string to alter the metric used to order the users.
By default, users are ordered by their computedFinalApplicationScore
, which is the max of their personal
score and their team's score.
{
status: 200,
message: [
// List of users who have been scored and graded, in descending order
// NOTE: To reduce the computational load, these users will be fetched directly
// from MongoDB and thus not go through the usual read/write checks + interceptors
]
}
/api/action/assignApplicationStatus
Assign the application status to users using the grading algorithm. The top n applicants (where n is the maximum number of accepted users) are assigned the accepted role, and the next m applicants (where m is the maximum number of waitlisted users) are assigned the waitlisted role. All remaining users who applied are rejected.
Warning: Once this action has executed and people are accepted, their spots will be reserved until it expires or it is manually revoked. Be very careful and ensure that everyone is graded prior to running this!
legit
can be used to indicate whether or not the changes will be
actually written to the database. If legit
is not true
, then it will essentially give a "preview"
into what the list would look like.
waitlistOver
can be set true
to reject all users that would otherwise be waitlisted / are on the waitlist
currently.
waitlistDeadline
will be used to set the confirmation deadline for users who were moved off the waitlist.
By default, this will be the system waitlist accepted confirmation deadline.
{
legit: <true|false>, // default false
waitlistOver: <true|false>, // default false
waitlistDeadline: <unix timestamp> // default disabled
}
{
status: 200,
message: {
"accepted": [
// Users
],
"waitlisted": [
// Users
],
"rejected": [
// Users
],
"dead": [
// Users
]
}
}
/api/action/rsvp
This endpoint is used to update a user's RSVP status. A user may only RSVP if they are accepted, not previously declined, and are in the RSVP period (whether global or personal). Once a user has declined, they cannot retract this decision.
{
"attending": true // false if they are not attending... duh,
"form": {
// RSVP form responses go here
}
}
{
status: 200,
message: "Success"
}
/api/action/getCandidate?category=<category goes here>
Fetch a random applicant that hasn't been graded yet.
category
can be optionally specified as a query parameter to only return candidates
who are ungraded in that category. If left blank, no category will be prioritized.
{
status: 200,
message: {
// User object goes here
}
}
/api/action/gradeCandidate
Assign a grade to a user in a particular category. The submitted grades will override any existing ones and the dictionary will be merged (so omitted fields are unchanged).
{
"candidateID": <id of user to grade>,
"grades": {
<category name>: <grade to assign the user>,
... // multiple categories can be graded simultaneously, if needed
}
}
{
status: 200,
message: "Success"
}
/api/action/verifyDiscord
Attempt to associate a user with a given Discord account. Will fail if the user does not exist or is already bound to another Discord account.
{
"email": <email of the user>,
"discordID": <Discord account ID>,
"discordUsername": <Discord account username (not checked, for display only)>
}
{
status: 200,
message: {
firstName: <first name of the user>,
lastName: <last name of the user>,
email: <email of the user>,
suffix: <Discord suffix of the user> (optional),
roles: [
<user Discord roles>
]
}
}
The user's Discord roles is taken from the user's permission roles as well as any in their discord.additionalRoles
field.
/api/action/resumeExport
Returns a ZIP of all the resumes of users who were accepted or waitlisted and consented to their resume being shared.
Binary blob
/api/action/disassociateDiscord
Disassociates a user from their associated Discord account.
{
userID: "<userID>"
}
{
status: 200,
message: "OK"
}
/api/action/disassociateDiscord
Returns the application's metadata that is stored by Discord.
{
userID: "<userID>"
}
{
status: 200,
message: {
...
}
}
/api/action/getNextQueuedDiscordVerification
Returns information about the next user that is in the Discord server verification queue. Note that this also removes the entry from the queue.
{
status: 200,
message: {
...
}
}
/api/action/requeueDiscordVerification
Requeue a Discord verification request.
{
queuedVerificationID: "<queueVerificationID>",
earliestRetryAt: "<unix time (ms)>"
}
{
status: 200,
message: ""
}
/api/action/addCheckInNotes
Adds the given check in notes to a user's check in notes. Returns all the user's new check in notes.
{
userID: "<userID>",
checkInNotes: ["<string>"]
}
{
status: 200,
message: ["<string>"]
}
/api/action/removeCheckInNotes
Remove the given check in notes to a user's check in notes. Returns all the user's new check in notes.
{
userID: "<userID>",
checkInNotes: ["<string>"]
}
{
status: 200,
message: ["<string>"]
}
/api/action/checkInQR
Returns a data URI with the user's check in code.
{
"status": 200,
"message": "<base64-encoded image>"
}
/api/action/checkIN
Checks in a (External) User.
{
"userID": "<userID>",
"userType": "<User|ExternalUser>"
}
{
"status": 200,
"message": false|[<Check-In Notes>]
}
/api/action/multiCheckInQR
Generates check in QR codes for multiple (external) users.
{
"userList": [{
"userID": "<userID>",
"userType: "<User|ExternalUser>"
}]
}
{
"status": 200,
"message": [{
"userID": "<userID>",
"userType": "<User|ExternalUser>"
"code": "<base64-encoded image>"
}]
}
/api/action/createAPIToken
Generates an API token that can be used to access the API programmatically.
{
"groups": ["<hacker|volunteer|organizer|admin>"],
"description": "<string>"
}
{
"status": 200,
"message": [{
"token": "<token>",
}]
}
/auth/:provider/login
provider
refers to the name of the OpenID provider being operated on.
{
"redirectTo": "<url to redirect the user to after authentication>", // the redirect is handled by frontend
"callbackURL": "<url of the OpenID callback endpoint>" // will default to provider.callback_url
}
callbackURL
should be set to the OpenID callback endpoint on the frontend. The frontend callback should then POST the code and state query parameters to the callback endpoint on the backend to retrieve the JWTs.
(e.g. dash.hackthe6ix.com/callback
, will direct the user to the main app after getting the tokens)
redirectTo
is simply passed to the callback endpoint on the frontend. Generally it will be where the user is redirected to after the JWTs are retrieved and the session is started.
{
"status": 200,
"message": {
"url": "<url to redirect the user to to begin auth flow>"
}
}
The flow state will be set to a JSON string. The callback URL is stored in the callbackURL
property and the redirect URL is stored in the redirectTo
property.
/auth/:provider/callback
provider
refers to the name of the OpenID provider being operated on.
{
"code": "<OpenID authorization code>",
"state": "<the flow state>"
}
{
"status": 200,
"message": {
"token": "<backend access token>",
"refreshToken": "<OpenID refresh token>",
"redirectTo": "<the url to redirect the user to> (optional)"
}
}
/auth/:provider/refresh
provider
refers to the name of the OpenID provider being operated on.
{
"refreshToken": "<refresh token goes here>"
}
{
status: 200,
message: {
token: ...,
refreshToken: ...
}
}
/auth/:provider/logout
provider
refers to the name of the OpenID provider being operated on.
{
"refreshToken": "<refresh token goes here>"
}
{
"status": 200
}