← Back to Guildbase

Guildbase REST API

A guild-scoped REST API for reading and managing your applications, members, roles and exams programmatically. All endpoints are versioned under /api/v1 and return JSON.

Base URL

https://guildbase.gg/api/v1

Auth

Bearer API key (Pro)

Rate limit

120 requests / minute / key

Authentication

Create an API key in Guild Settings → API Keys (requires a Pro subscription). Pass it as a Bearer token on every request. Keys are shown only once at creation — store them securely and never expose them in client-side code.

curl https://guildbase.gg/api/v1/me \
  -H "Authorization: Bearer gb_live_your_key_here" \
  -H "Accept: application/json"

A missing or invalid key returns 401:

{ "success": false, "error": "missing_api_key", "message": "API key is required. Use Authorization: Bearer <api_key>" }

Scopes

Each key is granted a set of scopes when it is created. A request to an endpoint whose scope the key lacks returns 403 insufficient_scope. The scope required by each endpoint is listed in the reference below.

guild:read Read guild profile
applications:read Read applications & templates
applications:write Approve, reject, move & delete applications
members:read Read guild members
members:write Add, update & remove members
roles:read Read guild roles
roles:write Create, update & delete roles
exams:read Read exams, attempts & certificates
exams:write Revoke certificates

Requests

Pagination

List endpoints are paginated. Use ?page= and ?per_page= (1–100, default 25). Every list response carries a meta object:

"meta": {
  "current_page": 1,
  "per_page": 25,
  "total": 128,
  "last_page": 6,
  "has_more": true
}

Rate limit headers

Responses include X-RateLimit-Limit and X-RateLimit-Remaining. When throttled you receive 429 with a Retry-After header.

Responses

Every response is wrapped in a consistent envelope.

Single object

{
  "success": true,
  "data": { "id": 1, "name": "..." }
}

Paginated list

{
  "success": true,
  "data": [ { "id": 1, ... }, { "id": 2, ... } ],
  "meta": { "current_page": 1, "per_page": 25,
            "total": 42, "last_page": 2, "has_more": true }
}

Fields marked "omitted" in the object reference are only present when the related data is loaded for that endpoint (for example, answers appears only on GET /applications/{id}).

Errors

Errors use the same envelope with success: false, a stable error code, and a human-readable message. Integrate against the code, not the message text.

Status Error code When it happens
401 missing_api_key No Authorization header, or it is not a Bearer token.
401 invalid_api_key_format The token does not begin with gb_live_.
401 invalid_api_key The token does not match any key.
401 api_key_invalid The key is inactive or has expired.
403 pro_required The guild that owns the key does not have an active Pro subscription.
403 insufficient_scope The key is valid but lacks the scope the endpoint requires.
403 template_not_published POST /sessions — the template is a draft, scheduled, closed or full. The current state is returned as template_status.
403 template_locked POST /sessions — the template is disabled by the guild's plan limit (e.g. after a downgrade).
404 not_found The resource does not exist, or belongs to a different guild.
404 template_not_found POST /sessions — no template in this guild matches the template_slug / template_id sent. List your templates with GET /api/v1/templates and copy the exact slug.
404 invalid_session POST /sessions/check — the session token does not exist.
410 session_invalid POST /sessions/check — the session has expired or has already been used.
405 method_not_allowed The HTTP verb is not supported for this path.
409 already_final Conflict — e.g. approving an application that is already accepted/rejected.
422 validation_failed The request body failed validation. Includes an errors object.
429 rate_limited Rate limit exceeded (120 requests / minute / key).
500 server_error An unexpected error occurred on our side.

Insufficient scope (403)

{
  "success": false,
  "error": "insufficient_scope",
  "message": "This API key is missing the required scope: members:write.",
  "required_scopes": ["members:write"]
}

Validation failed (422)

{
  "success": false,
  "error": "validation_failed",
  "message": "The given data was invalid.",
  "errors": {
    "stage_id": ["The stage id field is required."]
  }
}

In-game sessions

In-game scripts (FiveM, launchers, kiosks) let a player apply without signing in through Discord. Your script creates a session for the player's Discord id, then opens the returned embed_url in a browser or NUI frame. Sessions are single-use and expire after 1 hour.

1. Look up the template slug

Do this once, not on every request. Template slugs are generated with a random suffix (staff-application-a1b2c3) unless you set a custom slug in Template Settings — guessing a friendly name such as staff-app is the most common cause of 404 template_not_found.

curl https://guildbase.gg/api/v1/templates \
  -H "Authorization: Bearer gb_live_your_key_here" \
  -H "Accept: application/json"

2. Create the session

curl -X POST https://guildbase.gg/api/v1/sessions \
  -H "Authorization: Bearer gb_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"template_slug":"staff-application-a1b2c3","discord_id":"584378000000000000"}'
{
  "success": true,
  "session_token": "...",
  "embed_url": "https://guildbase.gg/embed/apply/your-guild/staff-application-a1b2c3?s=...",
  "expires_at": "2026-01-01T12:00:00+00:00",
  "template": { "id": 12, "name": "Staff Application", "slug": "staff-application-a1b2c3" }
}

3. Open the embed URL

Open embed_url verbatim — the ?s= token is what authenticates the player, so building the URL yourself will not work. Once the application is submitted the session is consumed; create a new one for the next player.

Troubleshooting a failed session request

  • 404 template_not_found — the slug or id does not exist in the guild that owns the API key. Re-check step 1; the response repeats the value it received as template_identifier.
  • 403 template_not_published — the template exists but is not live. The response includes template_status (draft, scheduled, closed, full).
  • 401 — the key is missing, malformed, inactive or expired.
  • 403 pro_required — the guild's Pro subscription is not active.
  • 422 validation_failed — usually discord_id; it must be the numeric id, not a username.

Log the response body, not just the status code — every error carries a message explaining the cause.

Endpoints

Introspection

GET /api/v1/me

Details about the current API key: guild, scopes and usage.

Returns: Key & guild metadata

GET /api/v1/guild guild:read

The guild this key is scoped to.

Returns: A Guild object

In-Game Sessions

POST /api/v1/sessions

Create a short-lived session so a player can fill in an application from inside your game/server (FiveM NUI, launcher, kiosk) without a Discord OAuth login. Returns an embed_url to open in the browser/NUI.

Parameter In Type Required Description
template_slug body string conditional Slug of the template to apply for. Required unless template_id is given. Copy it from GET /api/v1/templates — generated slugs end in a random suffix (e.g. staff-application-a1b2c3).
template_id body integer conditional Template id. Required unless template_slug is given.
discord_id body string required Discord user id of the player (digits only, max 20). The submitted application is attributed to this Discord account.

Returns: { session_token, embed_url, expires_at, template }

POST /api/v1/sessions/check

Check whether a session token is still valid (not expired, not yet used).

Parameter In Type Required Description
session_token body string required The session_token returned by POST /api/v1/sessions.

Returns: { session: { discord_id, template_id, expires_at } }

Application Templates

GET /api/v1/templates applications:read

List application templates for the guild.

Parameter In Type Required Description
is_active query boolean optional Filter to active/inactive templates only.
page query integer optional Page number (default 1).
per_page query integer optional Items per page, 1–100 (default 25).

Returns: Paginated list of ApplicationTemplate objects

GET /api/v1/templates/{id} applications:read

Retrieve a single template.

Returns: An ApplicationTemplate object

Applications

GET /api/v1/applications applications:read

List applications for the guild.

Parameter In Type Required Description
status query string optional Filter by status (pending, in_review, accepted, rejected, withdrawn).
template_id query integer optional Filter by application template id.
risk_level query string optional Filter by risk level (low, medium, high).
claimed query boolean optional Only claimed (true) or unclaimed (false) applications.
sort query string optional submitted_at (default) or created_at.
direction query string optional asc or desc (default).
page query integer optional Page number.
per_page query integer optional Items per page, 1–100.

Returns: Paginated list of Application objects (without answers)

GET /api/v1/applications/{id} applications:read

Retrieve one application, including its formatted answers.

Returns: An Application object (with answers)

POST /api/v1/applications/{id}/approve applications:write

Approve the application — moves it to the workflow's accepted final stage (or sets status to accepted) and fires the usual Discord notification.

Returns: The updated Application object

POST /api/v1/applications/{id}/reject applications:write

Reject the application.

Parameter In Type Required Description
reason body string optional Optional rejection reason (max 1000 chars), included in the notification.

Returns: The updated Application object

POST /api/v1/applications/{id}/move applications:write

Move the application to a specific workflow stage.

Parameter In Type Required Description
stage_id body integer required Target workflow stage id. Must belong to the application's workflow.
notes body string optional Optional note (max 1000 chars).

Returns: The updated Application object

DELETE /api/v1/applications/{id} applications:write

Permanently delete the application and its uploaded files.

Returns: { "deleted": true }

Members

GET /api/v1/members members:read

List guild members.

Parameter In Type Required Description
role_id query integer optional Filter by role id.
page query integer optional Page number.
per_page query integer optional Items per page, 1–100.

Returns: Paginated list of GuildMember objects

GET /api/v1/members/{id} members:read

Retrieve a member.

Returns: A GuildMember object

POST /api/v1/members members:write

Add an existing Guildbase user to the guild.

Parameter In Type Required Description
user_id body integer conditional Guildbase user id. Required unless discord_id is given.
discord_id body string conditional Discord user id. Required unless user_id is given.
role_id body integer optional Role to assign. Must belong to the guild.

Returns: The created GuildMember object (201)

PATCH /api/v1/members/{id} members:write

Change a member's role.

Parameter In Type Required Description
role_id body integer|null required New role id, or null to clear the role. Field must be present.

Returns: The updated GuildMember object

DELETE /api/v1/members/{id} members:write

Remove a member from the guild.

Returns: { "deleted": true }

Roles

GET /api/v1/roles roles:read

List guild roles with their permissions.

Parameter In Type Required Description
page query integer optional Page number.
per_page query integer optional Items per page, 1–100.

Returns: Paginated list of GuildRole objects

GET /api/v1/roles/{id} roles:read

Retrieve a role.

Returns: A GuildRole object

POST /api/v1/roles roles:write

Create a custom role.

Parameter In Type Required Description
name body string required Role name (max 50).
color body string required Hex colour, e.g. #8b5cf6.
permissions body string[] optional Array of permission keys (validated against the guild permission list).
discord_role_id body string optional Discord role id to map (must be unique within the guild).

Returns: The created GuildRole object (201)

PATCH /api/v1/roles/{id} roles:write

Update a role. Default roles (e.g. Owner) are locked and return 422.

Parameter In Type Required Description
name body string optional Role name (max 50).
color body string optional Hex colour.
permissions body string[] optional Replaces the role's permission set.
discord_role_id body string optional Discord role mapping.

Returns: The updated GuildRole object

DELETE /api/v1/roles/{id} roles:write

Delete a role. Default roles are locked and return 422.

Returns: { "deleted": true }

Exams & Certificates

GET /api/v1/exams exams:read

List exam templates.

Parameter In Type Required Description
page query integer optional Page number.
per_page query integer optional Items per page, 1–100.

Returns: Paginated list of ExamTemplate objects

GET /api/v1/exams/{id} exams:read

Retrieve an exam template.

Returns: An ExamTemplate object

GET /api/v1/exams/{id}/attempts exams:read

List attempts for an exam.

Parameter In Type Required Description
status query string optional Filter by attempt status (in_progress, submitted, graded, ...).
page query integer optional Page number.
per_page query integer optional Items per page, 1–100.

Returns: Paginated list of ExamAttempt objects

GET /api/v1/attempts/{id} exams:read

Retrieve a single attempt.

Returns: An ExamAttempt object

GET /api/v1/certificates exams:read

List issued certificates.

Parameter In Type Required Description
exam_template_id query integer optional Filter by exam template.
user_id query integer optional Filter by recipient user id.
valid query boolean optional true = not revoked, false = revoked.
page query integer optional Page number.
per_page query integer optional Items per page, 1–100.

Returns: Paginated list of ExamCertificate objects

GET /api/v1/certificates/{id} exams:read

Retrieve a certificate.

Returns: An ExamCertificate object

POST /api/v1/certificates/{id}/revoke exams:write

Revoke a certificate.

Parameter In Type Required Description
reason body string optional Optional revocation reason (max 500).

Returns: The updated ExamCertificate object

Objects

The shape of every resource returned by the API. Nested objects (like user or current_stage) reference the types below.

Guild

The guild a key is scoped to.

Field Type Description
id integer Guild id.
name string Display name.
slug string URL slug.
description string|null Guild description.
is_pro boolean Whether the guild has Pro.
member_count integer Member count (present on GET /guild).
created_at string (ISO 8601) Creation timestamp.
{
  "success": true,
  "data": {
    "id": 42,
    "name": "Nightfall Syndicate",
    "slug": "nightfall",
    "description": "Competitive roleplay community.",
    "is_pro": true,
    "member_count": 318,
    "created_at": "2026-01-04T18:22:10+00:00"
  }
}

ApplicationTemplate

A form definition applicants submit against.

Field Type Description
id integer Template id.
name string Template name.
slug string URL slug.
description string|null Description shown to applicants.
status string Computed: draft, live, closed, scheduled or full.
is_active boolean Whether the template is enabled.
is_public boolean Whether it is listed publicly.
access_mode string Who may apply (e.g. everyone, roles).
allow_multiple_submissions boolean Whether a user may submit more than once.
opens_at string|null ISO 8601 open time, or null.
closes_at string|null ISO 8601 close time, or null.
applications_count integer Number of applications (on list/show).
workflow_id integer|null Workflow this template feeds.
created_at string (ISO 8601) Creation timestamp.
{
  "success": true,
  "data": {
    "id": 7,
    "name": "Police Whitelist",
    "slug": "police-whitelist",
    "description": "Apply to join the PD.",
    "status": "live",
    "is_active": true,
    "is_public": true,
    "access_mode": "everyone",
    "allow_multiple_submissions": false,
    "opens_at": null,
    "closes_at": null,
    "applications_count": 128,
    "workflow_id": 12,
    "created_at": "2026-03-11T09:00:00+00:00"
  }
}

Application

A submitted application. The answers array is only included on the single-application endpoint.

Field Type Description
id string (uuid) Application id.
guild_id integer Owning guild id.
status string pending, in_review, accepted, rejected or withdrawn.
is_final boolean Whether the application is in a terminal state.
risk_level string|null Automated risk assessment (low/medium/high).
flags string[] Automated flags raised on the application.
revision_count integer How many times it has been resubmitted.
time_spent_seconds integer|null Time the applicant spent completing the form.
submitted_at string|null ISO 8601 submission time.
created_at string (ISO 8601) Creation timestamp.
updated_at string (ISO 8601) Last update timestamp.
template ApplicationTemplate|omitted Included when loaded (see obj-template).
user UserSummary|omitted The applicant (see obj-user).
current_stage WorkflowStage|omitted Current workflow stage (see obj-stage).
workflow_id integer|null Workflow id.
is_claimed boolean Whether a reviewer has claimed it.
claimed_by UserSummary|omitted Reviewer who claimed it.
claimed_at string|null ISO 8601 claim time.
answers Section[]|omitted Formatted answers. Only on GET /applications/{id}.
{
  "success": true,
  "data": {
    "id": "9b1deae4-2f6a-4c1e-9a0b-2e5f7c3a11d2",
    "guild_id": 42,
    "status": "in_review",
    "is_final": false,
    "risk_level": "low",
    "flags": [],
    "revision_count": 0,
    "time_spent_seconds": 214,
    "submitted_at": "2026-07-20T14:05:00+00:00",
    "created_at": "2026-07-20T14:01:26+00:00",
    "updated_at": "2026-07-20T15:10:03+00:00",
    "template": { "id": 7, "name": "Police Whitelist", "slug": "police-whitelist", "status": "live" },
    "user": {
      "id": 8, "username": "ecliptor", "display_name": "Ecliptor",
      "avatar_url": "https://cdn.discordapp.com/avatars/12.../a.png", "discord_id": "123456789012345678"
    },
    "current_stage": { "id": 3, "name": "Interview", "color": "#8b5cf6", "position": 2, "is_final": false, "final_status": null },
    "workflow_id": 12,
    "is_claimed": true,
    "claimed_by": { "id": 2, "username": "hr_lead", "display_name": "HR Lead", "avatar_url": null, "discord_id": "98765..." },
    "claimed_at": "2026-07-20T14:40:00+00:00",
    "answers": [
      {
        "title": "About you",
        "fields": [
          { "label": "Age", "type": "number", "value": "24", "formatted_value": "24" },
          { "label": "Why join?", "type": "textarea", "value": "I love RP.", "formatted_value": "I love RP." }
        ]
      }
    ]
  }
}

GuildMember

A user's membership within a guild.

Field Type Description
id integer Membership id (used in member endpoints).
guild_id integer Guild id.
role GuildRole|null|omitted Assigned role (see obj-role).
user UserSummary|omitted The user (see obj-user).
joined_at string|null ISO 8601 join time.
{
  "success": true,
  "data": {
    "id": 501,
    "guild_id": 42,
    "role": { "id": 4, "name": "Moderator", "color": "#22c55e", "position": 5, "is_default": false },
    "user": { "id": 8, "username": "ecliptor", "display_name": "Ecliptor", "avatar_url": null, "discord_id": "1234..." },
    "joined_at": "2026-05-02T11:30:00+00:00"
  }
}

GuildRole

A permission role within a guild.

Field Type Description
id integer Role id.
name string Role name.
color string Hex colour.
position integer Sort position.
is_default boolean Default roles (e.g. Owner) are locked from edit/delete.
discord_role_id string|null Mapped Discord role id.
permissions string[]|omitted Permission keys (present on role endpoints).
members_count integer Members with this role (on list/show).
created_at string (ISO 8601) Creation timestamp.
{
  "success": true,
  "data": {
    "id": 4,
    "name": "Moderator",
    "color": "#22c55e",
    "position": 5,
    "is_default": false,
    "discord_role_id": "1102938475610293847",
    "permissions": ["applications.view", "applications.review", "tickets.view"],
    "members_count": 12,
    "created_at": "2026-02-18T20:15:00+00:00"
  }
}

ExamTemplate

An exam definition.

Field Type Description
id integer Exam id.
name string Exam name.
slug string URL slug.
description string|null Description.
is_active boolean Whether the exam is enabled.
access_mode string Who may take it.
time_limit_minutes integer|null Time limit, or null for none.
max_attempts integer|null Attempt cap per user.
pass_mark number|null Pass threshold (percent).
certificate_enabled boolean Whether passing issues a certificate.
published_at string|null ISO 8601 publish time.
opens_at string|null ISO 8601 open time.
closes_at string|null ISO 8601 close time.
sections_count integer Section count (on list/show).
questions_count integer Question count (on list/show).
attempts_count integer Attempt count (on list/show).
created_at string (ISO 8601) Creation timestamp.
{
  "success": true,
  "data": {
    "id": 15,
    "name": "Driving Theory",
    "slug": "driving-theory",
    "description": "Rules of the road.",
    "is_active": true,
    "access_mode": "roles",
    "time_limit_minutes": 30,
    "max_attempts": 2,
    "pass_mark": 80,
    "certificate_enabled": true,
    "published_at": "2026-06-01T00:00:00+00:00",
    "opens_at": null,
    "closes_at": null,
    "sections_count": 3,
    "questions_count": 40,
    "attempts_count": 210,
    "created_at": "2026-05-20T12:00:00+00:00"
  }
}

ExamAttempt

A single user's attempt at an exam.

Field Type Description
id integer Attempt id.
guild_id integer Guild id.
exam_template_id integer Exam id.
attempt_number integer Nth attempt for this user.
status string in_progress, submitted, graded, expired, etc.
score number|null Raw score.
max_score number|null Maximum possible score.
percent number|null Score as a percentage.
grade_label string|null Grade band label.
requires_manual_grading boolean Whether manual grading is pending.
violation_count integer Anti-cheat violations recorded.
risk_level string|null Risk assessment.
started_at string|null ISO 8601 start time.
submitted_at string|null ISO 8601 submit time.
released_at string|null ISO 8601 results-release time.
time_spent_seconds integer|null Time spent.
exam ExamTemplate|omitted Included on the single-attempt endpoint.
user UserSummary|omitted The candidate.
{
  "success": true,
  "data": {
    "id": 9032,
    "guild_id": 42,
    "exam_template_id": 15,
    "attempt_number": 1,
    "status": "graded",
    "score": 34,
    "max_score": 40,
    "percent": 85,
    "grade_label": "Pass",
    "requires_manual_grading": false,
    "violation_count": 0,
    "risk_level": "low",
    "started_at": "2026-07-19T10:00:00+00:00",
    "submitted_at": "2026-07-19T10:24:00+00:00",
    "released_at": "2026-07-19T10:24:05+00:00",
    "time_spent_seconds": 1440,
    "user": { "id": 8, "username": "ecliptor", "display_name": "Ecliptor", "avatar_url": null, "discord_id": "1234..." }
  }
}

ExamCertificate

A certificate issued for a passing attempt.

Field Type Description
id integer Certificate id.
code string Public verification code.
guild_id integer Guild id.
exam_template_id integer Exam id.
exam_attempt_id integer Source attempt id.
recipient_name string Name printed on the certificate.
exam_name string Exam name snapshot.
score_percent number|null Score achieved.
grade_label string|null Grade band.
is_valid boolean Not expired and not revoked.
is_revoked boolean Whether it has been revoked.
issued_at string|null ISO 8601 issue time.
expires_at string|null ISO 8601 expiry, or null.
revoked_at string|null ISO 8601 revocation time.
revoked_reason string|null Reason supplied at revocation.
user UserSummary|omitted The recipient.
{
  "success": true,
  "data": {
    "id": 771,
    "code": "GB-CERT-4F9A2C",
    "guild_id": 42,
    "exam_template_id": 15,
    "exam_attempt_id": 9032,
    "recipient_name": "Ecliptor",
    "exam_name": "Driving Theory",
    "score_percent": 85,
    "grade_label": "Pass",
    "is_valid": true,
    "is_revoked": false,
    "issued_at": "2026-07-19T10:24:05+00:00",
    "expires_at": "2027-07-19T10:24:05+00:00",
    "revoked_at": null,
    "revoked_reason": null,
    "user": { "id": 8, "username": "ecliptor", "display_name": "Ecliptor", "avatar_url": null, "discord_id": "1234..." }
  }
}

UserSummary

A minimal, PII-safe view of a user embedded in other objects.

Field Type Description
id integer User id.
username string Guildbase username.
display_name string|null Display name.
avatar_url string|null Avatar URL.
discord_id string|null Linked Discord user id.
{
  "id": 8,
  "username": "ecliptor",
  "display_name": "Ecliptor",
  "avatar_url": "https://cdn.discordapp.com/avatars/12.../a.png",
  "discord_id": "123456789012345678"
}

WorkflowStage

A stage within an application workflow.

Field Type Description
id integer Stage id.
name string Stage name.
color string|null Hex colour.
position integer Order within the workflow.
is_final boolean Whether this is a terminal stage.
final_status string|null accepted or rejected when is_final is true.
{
  "id": 3,
  "name": "Interview",
  "color": "#8b5cf6",
  "position": 2,
  "is_final": false,
  "final_status": null
}