Developer documentation

GuildCore API

Connect your guild tools, manage a self-hosted license, or download a verified release. Find the right endpoint, check its access requirements, and copy a PHP or cURL example.

21 documented endpointshttps://api.guildcore.netJSON over HTTPS

Getting started

Choose the API for your task

Guild data and platform services use different hosts and credentials. Start with the versioned Guild API when building an integration for your guild.

Guild API

Use your guild’s HTTPS hostname with paths starting /api/v1. Your package must include API access. Create a key in Manager → Settings → API access.

Platform API

Use https://api.guildcore.net for licensing and releases. Get your self-hosted license key from your GuildCore account.

Shell · server environment
export GUILDCORE_API='https://api.guildcore.net'
export GUILDCORE_GUILD_ORIGIN='https://your-guild.guild.page'
# Set GUILDCORE_LICENSE_KEY or GUILDCORE_GUILD_API_KEY in your secret manager.

Base URLs have no trailing slash. Replace every example hostname, ID and token with your own value.

Example setup

Copy an example and make it yours

  • cURL: use a terminal with support for --fail-with-body. Session-based write examples also use jq to read the CSRF token.
  • PHP: use PHP 8 with the cURL extension. PHP reads the same environment variables shown in the shell setup.
  • Placeholders: replace example raid IDs, installation UUIDs, hostnames, versions and share tokens before sending a request.
  • Responses: check the HTTP status, then read the JSON body. Guild responses wrap the result in data; platform responses return their fields directly.

When an endpoint requires a guild session

Browser-session routes use the guild’s own session cookie. PHP and cURL examples read it from a private Netscape-format cookie jar that you provide. Sign in to the guild through its normal flow first; a GuildCore account cookie or API key will not substitute for that session. For an anonymous /api/session read, the cookie jar can start empty.

Shell · session examples only
export GUILDCORE_COOKIE_JAR='/private/guild-session.cookies'
# Use your own private file. Do not publish or share session cookies.

Session-based writes read a fresh CSRF token from /api/session and send it with the same session. JavaScript examples run on the guild’s origin. For a long-running integration, use the versioned Guild data API.

Examples are displayed as text. Copying code does not send a request. PHP and cURL keep HTTPS verification enabled and do not follow redirects.

Authentication

Access requirements

  • Guild API: send Authorization: Bearer <guild-api-key>.
  • Licensing and releases: send Authorization: Bearer <license-key>.
  • Browser routes: use the guild’s session cookie. Writes also require X-CSRF-Token from /api/session. The session and shared-plan reads allow anonymous requests; other routes require sign-in. Package features and demo restrictions apply.
  • PayPal webhooks: authenticate with PayPal’s delivery headers and signature.

Call the Guild API, licensing and release endpoints from your server over HTTPS. Requests with an Origin header are rejected. Keep keys out of browser code, URLs and source control. Admin and account cookies cannot authenticate these endpoints.

Guild API scopes

The key’s issuer must have portal.access, integrations.manage and the permission listed below. Keys also require the listed package features.

Scopes, issuer permissions and package features
ScopeGuild permissionPackage features
guild:readportal.accessAPI access
members:readguild.roster.viewAPI access
characters:readguild.roster.viewAPI access
raids:readraids.strategy.viewAPI access + Raid Planner

Choose 30, 90 or 365 days, or Never for no automatic expiry. The default is 90 days. Up to 20 active keys are allowed per issuer, including keys that never expire. All keys still require the issuer’s current permissions and package features. The full key is shown once. Revoke it in the same settings page when it is no longer needed; signing out does not revoke it.

Signed documents

Use the installation’s verifier and trusted signing keys to check entitlements, release offers and manifests, including their issuer, installation binding and validity. Decoding the JSON alone does not verify a signature.

Working with lists

Read the next page

The member, character and published-raid endpoints use an ID cursor. Each page contains data.items and data.next_after.

  1. Start with after=0 and choose a limit from 1 to 100. The default limit is 50.
  2. Process the returned items. If next_after is an ID, send it as the next request’s after value.
  3. Stop when next_after is null. Do not restart at zero on the last page.

The cursor is a record ID, not a page number. Each request applies the key’s current scopes, the issuer’s permissions and the guild’s package features.

Reference

Guild data API

Read-only endpoints on your guild’s hostname. Available to hosted guild.page portals and licensed self-hosted guilds with API access. GET and HEAD are supported; HEAD returns headers only.

GET/api/v1/guild#

Guild identity

Read your guild’s public identity, timezone, branding and links.

AccessGuild API key · guild:readGuild hostname

No request parameters.

Request examples

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_GUILD_API_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/v1/guild" \
  --header "Authorization: Bearer $GUILDCORE_GUILD_API_KEY"
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_GUILD_API_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/api/v1/guild';

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
Example response 200 OK
JSON · response excerpt
{
    "success": true,
    "data": {
        "name": "Dawnwatch",
        "description": "A home for our raid team.",
        "timezone": "Europe/Oslo",
        "locale": "en-GB"
    },
    "message": null
}
GET/api/v1/members#

Member directory

Read members whose member and linked raider records are active, trial or on vacation. Private notes, Discord IDs and account details are excluded.

AccessGuild API key · members:readGuild hostname

Request parameters
ParameterLocation / typeRequirementDescription
afterqueryintegerOptionalNon-negative ID cursor; default 0. Results have IDs greater than this value. Use next_after for the next page; null means the last page.
limitqueryintegerOptionalNumber of records, 1–100. Default 50.

Request examples

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_GUILD_API_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/v1/members" \
  --header "Authorization: Bearer $GUILDCORE_GUILD_API_KEY" \
  --data-urlencode 'after=0' \
  --data-urlencode 'limit=50'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_GUILD_API_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/api/v1/members';
$query = [
    'after' => '0',
    'limit' => '50',
];
$url .= '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
Example response 200 OK
JSON · example response
{
    "success": true,
    "data": {
        "items": [
            {
                "id": 42,
                "display_name": "Aelira"
            }
        ],
        "next_after": null
    },
    "message": null
}
GET/api/v1/characters#

Character directory

Read active characters whose member and linked raider records are active, trial or on vacation. Includes character IDs, names, classes, specs, roles, realms and main-character flags.

AccessGuild API key · characters:readGuild hostname

Request parameters
ParameterLocation / typeRequirementDescription
afterqueryintegerOptionalNon-negative ID cursor; default 0. Results have IDs greater than this value. Use next_after for the next page; null means the last page.
limitqueryintegerOptionalNumber of records, 1–100. Default 50.

Request examples

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_GUILD_API_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/v1/characters" \
  --header "Authorization: Bearer $GUILDCORE_GUILD_API_KEY" \
  --data-urlencode 'after=0' \
  --data-urlencode 'limit=50'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_GUILD_API_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/api/v1/characters';
$query = [
    'after' => '0',
    'limit' => '50',
];
$url .= '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
Example response 200 OK
JSON · example response
{
    "success": true,
    "data": {
        "items": [
            {
                "id": 7,
                "name": "Aelira",
                "class": "Priest",
                "spec": "Holy",
                "role": "healer",
                "realm": "Forever",
                "is_main": true
            }
        ],
        "next_after": null
    },
    "message": null
}
GET/api/v1/raids#

Published raids

List published raid IDs, names, start times and publication times. Draft and removed raids are excluded.

AccessGuild API key · raids:readGuild hostname

Request parameters
ParameterLocation / typeRequirementDescription
afterqueryintegerOptionalNon-negative ID cursor; default 0. Results have IDs greater than this value. Use next_after for the next page; null means the last page.
limitqueryintegerOptionalNumber of records, 1–100. Default 50.

Request examples

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_GUILD_API_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/v1/raids" \
  --header "Authorization: Bearer $GUILDCORE_GUILD_API_KEY" \
  --data-urlencode 'after=0' \
  --data-urlencode 'limit=50'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_GUILD_API_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/api/v1/raids';
$query = [
    'after' => '0',
    'limit' => '50',
];
$url .= '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
Example response 200 OK
JSON · example response
{
    "success": true,
    "data": {
        "items": [
            {
                "id": 42,
                "name": "Sunday Molten Core",
                "start_time": "2026-10-18 17:00:00",
                "published_at": "2026-10-17 10:00:00"
            }
        ],
        "next_after": null
    },
    "message": null
}
GET/api/v1/raids/{id}/plan#

Published raid plan

Read the latest published composition, encounters, strategies and assignments. Raid-Helper availability reflects the latest sync. A share link is not required. Drafts, private Discord IDs and editor history are excluded.

AccessGuild API key · raids:readGuild hostname

Request parameters
ParameterLocation / typeRequirementDescription
idpathintegerRequiredID of a published raid in this guild.

Request examples

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_GUILD_API_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/v1/raids/42/plan" \
  --header "Authorization: Bearer $GUILDCORE_GUILD_API_KEY"
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_GUILD_API_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/api/v1/raids/42/plan';

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
Example response 200 OK
JSON · response excerpt
{
    "success": true,
    "data": {
        "raid": {
            "name": "Sunday Molten Core",
            "start_time": "2026-10-18 17:00:00"
        },
        "players": [
            {
                "key": "signup:7",
                "name": "Aelira",
                "class": "Priest",
                "role": "healer",
                "spec": "Holy",
                "signup_status": "confirmed",
                "current_signup_status": "confirmed",
                "signup_source": "local"
            }
        ],
        "encounters": []
    },
    "message": null
}

Reference

Self-hosted licenses

Self-hosted license management. All endpoints require a license key. Domain challenges, activation and refresh also require an active, verified subscription. The expires_at and refresh_after fields are Unix timestamps in seconds.

POST/v1/licenses/challenge#

Verify your domain

Request a DNS challenge for your license before activation. Add the returned TXT record, then activate with the returned proof within 10 minutes. The hostname must be at most 233 characters.

AccessLicense bearer keyAPI hostname

Request parameters
ParameterLocation / typeRequirementDescription
installation_uuidbodystring · UUIDRequiredThe installation’s permanent UUID, in lowercase canonical form.
hostnamebodystringRequiredYour lowercase public DNS hostname, without a scheme, port or path. GuildCore domains are reserved.

Request examples

cURL · terminal
: "${GUILDCORE_API:?Set the HTTPS base URL first}"
: "${GUILDCORE_LICENSE_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --request POST "$GUILDCORE_API/v1/licenses/challenge" \
  --header "Authorization: Bearer $GUILDCORE_LICENSE_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "installation_uuid": "11111111-1111-4111-8111-111111111111",
    "hostname": "guild.example.com"
}'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_API') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_LICENSE_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/v1/licenses/challenge';
$headers[] = 'Content-Type: application/json';
$payload = json_encode([
    'installation_uuid' => '11111111-1111-4111-8111-111111111111',
    'hostname' => 'guild.example.com',
], JSON_THROW_ON_ERROR);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $payload,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);

print_r($result);
Example response 200 OK
JSON · example response
{
    "proof": "<signed domain proof>",
    "record": "_guildpage-license.guild.example.com",
    "type": "TXT",
    "value": "guildpage-verification=<sha256 of proof>",
    "expires_at": 1791029400
}
POST/v1/licenses/activate#

Activate an installation

Bind a license to an installation and verified domain, and return a signed entitlement. If already activated, the installation UUID and hostname must match. Deactivate the license before moving it.

AccessLicense bearer keyAPI hostname

Request parameters
ParameterLocation / typeRequirementDescription
installation_uuidbodystring · UUIDRequiredThe installation’s permanent UUID, in lowercase canonical form.
hostnamebodystringRequiredYour lowercase public DNS hostname, without a scheme, port or path. GuildCore domains are reserved.
versionbodystringRequiredInstalled application version; 1–64 letters, digits, dots, underscores, plus signs or hyphens, starting with a letter or digit.
request_idbodystring · UUIDRequiredA new lowercase UUID for this operation. Reuse it when retrying the same lease request.
domain_proofbodystringFirst activationThe unmodified proof returned by the challenge endpoint. DNS verification must succeed.

Request examples

cURL · terminal
: "${GUILDCORE_API:?Set the HTTPS base URL first}"
: "${GUILDCORE_LICENSE_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --request POST "$GUILDCORE_API/v1/licenses/activate" \
  --header "Authorization: Bearer $GUILDCORE_LICENSE_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "installation_uuid": "11111111-1111-4111-8111-111111111111",
    "hostname": "guild.example.com",
    "version": "1.0.0",
    "request_id": "22222222-2222-4222-8222-222222222222",
    "domain_proof": "<proof from challenge>"
}'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_API') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_LICENSE_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/v1/licenses/activate';
$headers[] = 'Content-Type: application/json';
$payload = json_encode([
    'installation_uuid' => '11111111-1111-4111-8111-111111111111',
    'hostname' => 'guild.example.com',
    'version' => '1.0.0',
    'request_id' => '22222222-2222-4222-8222-222222222222',
    'domain_proof' => '<proof from challenge>',
], JSON_THROW_ON_ERROR);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $payload,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);

print_r($result);
Example response 200 OK
JSON · example response
{
    "entitlement": "<signed entitlement document>",
    "license_uuid": "33333333-3333-4333-8333-333333333333",
    "refresh_after": 1791025200
}
POST/v1/licenses/refresh#

Refresh entitlements

Renew the signed entitlement for an activated license. The installation UUID and hostname must match the activation. Entitlements cannot extend beyond the paid subscription period.

AccessLicense bearer keyAPI hostname

Request parameters
ParameterLocation / typeRequirementDescription
installation_uuidbodystring · UUIDRequiredThe installation’s permanent UUID, in lowercase canonical form.
hostnamebodystringRequiredYour lowercase public DNS hostname, without a scheme, port or path. GuildCore domains are reserved.
versionbodystringRequiredInstalled application version; 1–64 letters, digits, dots, underscores, plus signs or hyphens, starting with a letter or digit.
request_idbodystring · UUIDRequiredA new lowercase UUID for this operation. Reuse it when retrying the same lease request.

Request examples

cURL · terminal
: "${GUILDCORE_API:?Set the HTTPS base URL first}"
: "${GUILDCORE_LICENSE_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --request POST "$GUILDCORE_API/v1/licenses/refresh" \
  --header "Authorization: Bearer $GUILDCORE_LICENSE_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "installation_uuid": "11111111-1111-4111-8111-111111111111",
    "hostname": "guild.example.com",
    "version": "1.0.0",
    "request_id": "22222222-2222-4222-8222-222222222222"
}'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_API') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_LICENSE_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/v1/licenses/refresh';
$headers[] = 'Content-Type: application/json';
$payload = json_encode([
    'installation_uuid' => '11111111-1111-4111-8111-111111111111',
    'hostname' => 'guild.example.com',
    'version' => '1.0.0',
    'request_id' => '22222222-2222-4222-8222-222222222222',
], JSON_THROW_ON_ERROR);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $payload,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);

print_r($result);
Example response 200 OK
JSON · example response
{
    "entitlement": "<signed entitlement document>",
    "license_uuid": "33333333-3333-4333-8333-333333333333",
    "refresh_after": 1791025200
}
GET/v1/licenses/status#

Read license status

Read the license status: active or inactive. paid_until is null when inactive; last_validation can be null. Use the signed entitlement to authorize package features.

AccessLicense bearer keyAPI hostname

Request parameters
ParameterLocation / typeRequirementDescription
installation_uuidquerystring · UUIDRequiredThe installation’s permanent UUID, in lowercase canonical form.
hostnamequerystringRequiredYour lowercase public DNS hostname, without a scheme, port or path. GuildCore domains are reserved.

Request examples

cURL · terminal
: "${GUILDCORE_API:?Set the HTTPS base URL first}"
: "${GUILDCORE_LICENSE_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_API/v1/licenses/status" \
  --header "Authorization: Bearer $GUILDCORE_LICENSE_KEY" \
  --data-urlencode 'installation_uuid=11111111-1111-4111-8111-111111111111' \
  --data-urlencode 'hostname=guild.example.com'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_API') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_LICENSE_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/v1/licenses/status';
$query = [
    'installation_uuid' => '11111111-1111-4111-8111-111111111111',
    'hostname' => 'guild.example.com',
];
$url .= '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);

print_r($result);
Example response 200 OK
JSON · example response
{
    "license_uuid": "33333333-3333-4333-8333-333333333333",
    "installation_uuid": "11111111-1111-4111-8111-111111111111",
    "status": "active",
    "last_validation": "2026-10-03 10:00:00.000000",
    "paid_until": "2026-11-03 10:00:00.000000",
    "sequence": 12
}
POST/v1/licenses/deactivate#

Deactivate an installation

Release the license from its installation and hostname. Guild data is preserved. Repeating the request after deactivation returns the same success response.

AccessLicense bearer keyAPI hostname

Request parameters
ParameterLocation / typeRequirementDescription
installation_uuidbodystring · UUIDRequiredThe installation’s permanent UUID, in lowercase canonical form.
hostnamebodystringRequiredYour lowercase public DNS hostname, without a scheme, port or path. GuildCore domains are reserved.

Request examples

cURL · terminal
: "${GUILDCORE_API:?Set the HTTPS base URL first}"
: "${GUILDCORE_LICENSE_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --request POST "$GUILDCORE_API/v1/licenses/deactivate" \
  --header "Authorization: Bearer $GUILDCORE_LICENSE_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "installation_uuid": "11111111-1111-4111-8111-111111111111",
    "hostname": "guild.example.com"
}'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_API') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_LICENSE_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/v1/licenses/deactivate';
$headers[] = 'Content-Type: application/json';
$payload = json_encode([
    'installation_uuid' => '11111111-1111-4111-8111-111111111111',
    'hostname' => 'guild.example.com',
], JSON_THROW_ON_ERROR);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $payload,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);

print_r($result);
Example response 200 OK
JSON · example response
{
    "status": "deactivated"
}

Reference

Release updates

Self-hosted updates. Requires a license key, a matching installation and hostname, and an active, verified subscription. GET and HEAD are supported; HEAD returns headers only.

GET/v1/releases/latest#

Check for a release

Return a signed offer containing the latest release manifest. release_document is null if no release exists or the latest is older than current_version. The same version may be returned; check the manifest version and minimum_version before upgrading.

AccessLicense bearer keyAPI hostname

Request parameters
ParameterLocation / typeRequirementDescription
installation_uuidquerystring · UUIDRequiredThe installation’s permanent UUID, in lowercase canonical form.
hostnamequerystringRequiredYour lowercase public DNS hostname, without a scheme, port or path. GuildCore domains are reserved.
request_idquerystring · UUIDRequiredA new lowercase UUID, included in the signed offer.
current_versionquerystring · versionRequiredThe installed semantic version.
channelquerystringRequiredstable selects stable releases; prerelease includes both stable and prerelease versions.

Request examples

cURL · terminal
: "${GUILDCORE_API:?Set the HTTPS base URL first}"
: "${GUILDCORE_LICENSE_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_API/v1/releases/latest" \
  --header "Authorization: Bearer $GUILDCORE_LICENSE_KEY" \
  --data-urlencode 'installation_uuid=11111111-1111-4111-8111-111111111111' \
  --data-urlencode 'hostname=guild.example.com' \
  --data-urlencode 'request_id=22222222-2222-4222-8222-222222222222' \
  --data-urlencode 'current_version=1.0.0' \
  --data-urlencode 'channel=stable'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_API') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$key = getenv('GUILDCORE_LICENSE_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/v1/releases/latest';
$query = [
    'installation_uuid' => '11111111-1111-4111-8111-111111111111',
    'hostname' => 'guild.example.com',
    'request_id' => '22222222-2222-4222-8222-222222222222',
    'current_version' => '1.0.0',
    'channel' => 'stable',
];
$url .= '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);

print_r($result);
Example response 200 OK
JSON · example response
{
    "authorization": "<signed release-offer document>"
}
GET/v1/releases/{release_uuid}/download#

Download a release

Download the ZIP identified by a verified release manifest. Check the downloaded size and SHA-256 against the manifest before installation.

AccessLicense bearer keyAPI hostname

Request parameters
ParameterLocation / typeRequirementDescription
release_uuidpathstring · UUIDRequiredLowercase release UUID from the verified manifest.
installation_uuidquerystring · UUIDRequiredThe installation’s permanent UUID, in lowercase canonical form.
hostnamequerystringRequiredYour lowercase public DNS hostname, without a scheme, port or path. GuildCore domains are reserved.

Request examples

cURL · terminal
: "${GUILDCORE_API:?Set the HTTPS base URL first}"
: "${GUILDCORE_LICENSE_KEY:?Set your key in the server environment}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 300 \
  --get "$GUILDCORE_API/v1/releases/<release-uuid>/download" \
  --header "Authorization: Bearer $GUILDCORE_LICENSE_KEY" \
  --data-urlencode 'installation_uuid=11111111-1111-4111-8111-111111111111' \
  --data-urlencode 'hostname=guild.example.com' \
  --output GuildCore-release.zip.unverified

# Do not install this file until its size and SHA-256 match a verified release manifest.
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_API') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/zip'];
$key = getenv('GUILDCORE_LICENSE_KEY') ?: '';
if ($key === '') throw new RuntimeException('Configure your API key first.');
$headers[] = 'Authorization: Bearer ' . $key;

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 300,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/v1/releases/<release-uuid>/download';
$query = [
    'installation_uuid' => '11111111-1111-4111-8111-111111111111',
    'hostname' => 'guild.example.com',
];
$url .= '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);
$file = fopen('GuildCore-release.zip.unverified', 'xb');
if ($file === false) throw new RuntimeException('Cannot create the download file.');

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_FILE => $file,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);
fclose($file);

if ($body === false || $status < 200 || $status >= 300) {
    unlink('GuildCore-release.zip.unverified');
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
// Verify this file's size and SHA-256 against the signed manifest before use.
Example response 200 OK · application/zip

A binary ZIP download. The response includes Content-Disposition: attachment. HEAD returns only headers.

Reference

PayPal webhooks

Platform operators configure PayPal to deliver events to the matching sandbox or live URL. Guild integrations do not send payment events.

POST/v1/webhooks/paypal/{environment}#

Receive PayPal events

Use /v1/webhooks/paypal/sandbox for test deliveries and /v1/webhooks/paypal/live for live deliveries. Verified events are queued for processing. Duplicate deliveries return the original receipt_uuid with status duplicate. The legacy /v1/webhooks/paypal URL selects the environment from the PayPal certificate hostname; the matching PayPal app must still verify the signature.

AccessVerified PayPal deliveryAPI hostname

Request parameters
ParameterLocation / typeRequirementDescription
environmentpathstringRequiredsandbox or live; must match the configured PayPal app and original delivery.
Content-TypeheaderstringRequiredapplication/json; maximum body size 256 KiB.
PAYPAL-AUTH-ALGOheaderstringRequiredSHA256withRSA.
PAYPAL-CERT-URLheaderHTTPS URLRequiredPayPal certificate URL for the configured environment.
PAYPAL-TRANSMISSION-IDheaderstringRequiredProvider transmission ID.
PAYPAL-TRANSMISSION-SIGheaderstringRequiredProvider transmission signature.
PAYPAL-TRANSMISSION-TIMEheadertimestampRequiredProvider transmission timestamp.
idbodystringRequiredUnique PayPal event ID.
event_typebodystringRequiredPayPal subscription or sale event type.
create_timebodyISO 8601 timestampRequiredEvent creation time.
resourcebodyobjectRequiredThe original provider resource. Preserve the complete delivery payload.

Request examples

The PHP and cURL examples replay a recent, authentic delivery using its original body and all five PayPal headers. The illustrative payload alone cannot pass signature verification. The examples use the sandbox URL.

cURL · terminal
: "${GUILDCORE_API:?Set the HTTPS base URL first}"
# Replay an authentic delivery only; keep its original body and headers.
: "${PAYPAL_WEBHOOK_BODY_FILE:?Set the path to the original JSON body}"
: "${PAYPAL_AUTH_ALGO:?Set the original PayPal delivery header}"
: "${PAYPAL_CERT_URL:?Set the original PayPal delivery header}"
: "${PAYPAL_TRANSMISSION_ID:?Set the original PayPal delivery header}"
: "${PAYPAL_TRANSMISSION_SIG:?Set the original PayPal delivery header}"
: "${PAYPAL_TRANSMISSION_TIME:?Set the original PayPal delivery header}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --request POST "$GUILDCORE_API/v1/webhooks/paypal/sandbox" \
  --header 'Content-Type: application/json' \
  --header "PAYPAL-AUTH-ALGO: $PAYPAL_AUTH_ALGO" \
  --header "PAYPAL-CERT-URL: $PAYPAL_CERT_URL" \
  --header "PAYPAL-TRANSMISSION-ID: $PAYPAL_TRANSMISSION_ID" \
  --header "PAYPAL-TRANSMISSION-SIG: $PAYPAL_TRANSMISSION_SIG" \
  --header "PAYPAL-TRANSMISSION-TIME: $PAYPAL_TRANSMISSION_TIME" \
  --data-binary "@$PAYPAL_WEBHOOK_BODY_FILE"
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_API') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/v1/webhooks/paypal/sandbox';
$headers[] = 'Content-Type: application/json';
// Use the original delivery bytes; do not rebuild or reformat the JSON.
$bodyFile = getenv('PAYPAL_WEBHOOK_BODY_FILE') ?: '';
if ($bodyFile === '' || !is_readable($bodyFile)) {
    throw new RuntimeException('Provide the original PayPal delivery body.');
}
$payload = file_get_contents($bodyFile);
if ($payload === false) throw new RuntimeException('Could not read the delivery body.');
$value = getenv('PAYPAL_AUTH_ALGO') ?: '';
if ($value === '') throw new RuntimeException('Missing PayPal delivery header.');
$headers[] = 'PAYPAL-AUTH-ALGO: ' . $value;
$value = getenv('PAYPAL_CERT_URL') ?: '';
if ($value === '') throw new RuntimeException('Missing PayPal delivery header.');
$headers[] = 'PAYPAL-CERT-URL: ' . $value;
$value = getenv('PAYPAL_TRANSMISSION_ID') ?: '';
if ($value === '') throw new RuntimeException('Missing PayPal delivery header.');
$headers[] = 'PAYPAL-TRANSMISSION-ID: ' . $value;
$value = getenv('PAYPAL_TRANSMISSION_SIG') ?: '';
if ($value === '') throw new RuntimeException('Missing PayPal delivery header.');
$headers[] = 'PAYPAL-TRANSMISSION-SIG: ' . $value;
$value = getenv('PAYPAL_TRANSMISSION_TIME') ?: '';
if ($value === '') throw new RuntimeException('Missing PayPal delivery header.');
$headers[] = 'PAYPAL-TRANSMISSION-TIME: ' . $value;

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $payload,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);

print_r($result);
JSON · illustrative PayPal payload
{
    "id": "WH-EXAMPLE-ONLY",
    "event_type": "BILLING.SUBSCRIPTION.ACTIVATED",
    "create_time": "2026-10-03T10:00:00Z",
    "resource": {
        "id": "I-EXAMPLEONLY"
    }
}
Example response 202 Accepted · 200 for duplicates
JSON · example response
{
    "receipt_uuid": "44444444-4444-4444-8444-444444444444",
    "status": "queued"
}

Reference

Browser sessions and data

Internal routes used by the guild portal. Call these on the guild hostname with a browser session. The permissions shown below and the guild’s package features apply. Use the versioned Guild API for server integrations.

GET/api/session#

Read the current session

Read the current user, permissions, package entitlements and session CSRF token. Anonymous requests return user: null and an empty permissions list.

AccessAnonymous or signed-in sessionGuild hostname

No request parameters.

Request examples

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_COOKIE_JAR:?Set a private cookie jar path; authenticated routes need a signed-in session}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/session" \
  --cookie "$GUILDCORE_COOKIE_JAR" \
  --cookie-jar "$GUILDCORE_COOKIE_JAR"
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$cookieJar = getenv('GUILDCORE_COOKIE_JAR') ?: '';
// A cookie jar is optional for an anonymous session read.

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);
if ($cookieJar !== '') {
    curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieJar);
    curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieJar);
}

$url = $origin . '/api/session';

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
JavaScript · guild origin
// Run on the guild's origin. Sign-in is optional.
const response = await fetch('/api/session', {
  credentials: 'same-origin'
});
const result = await response.json();
if (!response.ok || !result.success) {
  throw new Error(result.message || 'Request failed');
}
console.log(result.data);
Example response 200 OK
JSON · response excerpt
{
    "success": true,
    "data": {
        "user": null,
        "permissions": [],
        "csrf": "<session-bound CSRF token>"
    },
    "message": null
}
GET/api/guild#

Read the guild directory

Read active members and their main character. Character fields are null when no main character is assigned.

Accessguild.roster.viewGuild hostname

No request parameters.

Request examples

PHP and cURL require your signed-in guild session in a private cookie jar. A Guild API key cannot authenticate this route. Example setup

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_COOKIE_JAR:?Set a private cookie jar path; authenticated routes need a signed-in session}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/guild" \
  --cookie "$GUILDCORE_COOKIE_JAR" \
  --cookie-jar "$GUILDCORE_COOKIE_JAR"
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$cookieJar = getenv('GUILDCORE_COOKIE_JAR') ?: '';
if ($cookieJar === '' || !is_readable($cookieJar)) {
    throw new RuntimeException('Provide your signed-in guild cookie jar.');
}

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);
if ($cookieJar !== '') {
    curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieJar);
    curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieJar);
}

$url = $origin . '/api/guild';

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
JavaScript · guild origin
// Run in your signed-in guild portal.
const response = await fetch('/api/guild', {
  credentials: 'same-origin'
});
const result = await response.json();
if (!response.ok || !result.success) {
  throw new Error(result.message || 'Request failed');
}
console.log(result.data);
Example response 200 OK
JSON · example response
{
    "success": true,
    "data": [
        {
            "id": 42,
            "display_name": "Aelira",
            "access_role": "member",
            "character_id": 7,
            "name": "Aelira",
            "class": "Priest",
            "spec": "Holy",
            "role": "healer",
            "realm": "Forever"
        }
    ],
    "message": null
}
GET/api/raids#

List raids

List raids visible to the signed-in member. Managers receive draft metadata. Other members receive published raid details; loot.view also permits basic details for unpublished raids.

Accessraids.viewGuild hostname

No request parameters.

Request examples

PHP and cURL require your signed-in guild session in a private cookie jar. A Guild API key cannot authenticate this route. Example setup

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_COOKIE_JAR:?Set a private cookie jar path; authenticated routes need a signed-in session}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/raids" \
  --cookie "$GUILDCORE_COOKIE_JAR" \
  --cookie-jar "$GUILDCORE_COOKIE_JAR"
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$cookieJar = getenv('GUILDCORE_COOKIE_JAR') ?: '';
if ($cookieJar === '' || !is_readable($cookieJar)) {
    throw new RuntimeException('Provide your signed-in guild cookie jar.');
}

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);
if ($cookieJar !== '') {
    curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieJar);
    curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieJar);
}

$url = $origin . '/api/raids';

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
JavaScript · guild origin
// Run in your signed-in guild portal.
const response = await fetch('/api/raids', {
  credentials: 'same-origin'
});
const result = await response.json();
if (!response.ok || !result.success) {
  throw new Error(result.message || 'Request failed');
}
console.log(result.data);
Example response 200 OK
JSON · response excerpt
{
    "success": true,
    "data": [
        {
            "id": 42,
            "name": "Sunday Molten Core",
            "start_time": "2026-10-18 17:00:00",
            "published_version": 1,
            "sync_status": "published"
        }
    ],
    "message": null
}
POST/api/raids#

Create a local raid

Create a raid in the current guild. Including wow_loot_zone_id also creates a loot run and adds a loot object to the response. That option requires loot.softres.manage.

Accessraids.manage + CSRFGuild hostname

Request parameters
ParameterLocation / typeRequirementDescription
namebodystringRequiredRaid title, up to 150 characters.
start_timebodystring · date/timeRequiredUp to 40 characters. Use ISO 8601 with a timezone, for example 2026-10-18T17:00:00Z.
wow_loot_zone_idbodyintegerOptionalAn available loot instance ID; requires loot.softres.manage.

Request examples

PHP and cURL require your signed-in guild session in a private cookie jar. A Guild API key cannot authenticate this route. Example setup

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_COOKIE_JAR:?Set a private cookie jar path; authenticated routes need a signed-in session}"

# Fetch a CSRF token using the same signed-in session. Requires jq.
session=$(curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --cookie "$GUILDCORE_COOKIE_JAR" --cookie-jar "$GUILDCORE_COOKIE_JAR" \
  "$GUILDCORE_GUILD_ORIGIN/api/session") || exit 1
csrf=$(printf '%s' "$session" | jq -er '.data.csrf | select(type == "string" and length > 0)') || exit 1

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --request POST "$GUILDCORE_GUILD_ORIGIN/api/raids" \
  --cookie "$GUILDCORE_COOKIE_JAR" \
  --cookie-jar "$GUILDCORE_COOKIE_JAR" \
  --header "X-CSRF-Token: $csrf" \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Sunday Molten Core",
    "start_time": "2026-10-18T17:00:00Z"
}'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$cookieJar = getenv('GUILDCORE_COOKIE_JAR') ?: '';
if ($cookieJar === '' || !is_readable($cookieJar)) {
    throw new RuntimeException('Provide your signed-in guild cookie jar.');
}

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);
if ($cookieJar !== '') {
    curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieJar);
    curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieJar);
}

// Read CSRF using the same cURL handle and signed-in guild session.
curl_setopt($curl, CURLOPT_URL, $origin . '/api/session');
$sessionBody = curl_exec($curl);
$sessionStatus = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
if ($sessionBody === false || $sessionStatus !== 200) {
    throw new RuntimeException('Could not read the guild session.');
}
$session = json_decode($sessionBody, true, 32, JSON_THROW_ON_ERROR);
$csrf = $session['data']['csrf'] ?? '';
if (!is_string($csrf) || $csrf === '') {
    throw new RuntimeException('The session did not return a CSRF token.');
}
$headers[] = 'X-CSRF-Token: ' . $csrf;

$url = $origin . '/api/raids';
$headers[] = 'Content-Type: application/json';
$payload = json_encode([
    'name' => 'Sunday Molten Core',
    'start_time' => '2026-10-18T17:00:00Z',
], JSON_THROW_ON_ERROR);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $payload,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
JavaScript · guild origin
// Run in your signed-in guild portal.
const sessionResponse = await fetch('/api/session', {
  credentials: 'same-origin'
});
if (!sessionResponse.ok) throw new Error('Could not read session');
const session = await sessionResponse.json();

const response = await fetch('/api/raids', {
  method: 'POST',
  credentials: 'same-origin',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': session.data.csrf
  },
  body: JSON.stringify({
      "name": "Sunday Molten Core",
      "start_time": "2026-10-18T17:00:00Z"
  })
});
const result = await response.json();
if (!response.ok || !result.success) {
  throw new Error(result.message || 'Request failed');
}
console.log(result.data);
Example response 201 Created
JSON · example response
{
    "success": true,
    "data": {
        "id": 42
    },
    "message": null
}

Reference

Raid sharing

Requires Raid Planner. Anyone with a share link can read its published plan. Creating, viewing or revoking a link through /api/raids/{id}/share requires a guild session with raids.plan.publish.

GET/api/raids/{id}/share#

Read a share link

Return the current share path, or null if no link exists or the plan is unpublished.

Accessraids.plan.publishGuild hostname

Request parameters
ParameterLocation / typeRequirementDescription
idpathintegerRequiredRaid ID in the current guild.

Request examples

PHP and cURL require your signed-in guild session in a private cookie jar. A Guild API key cannot authenticate this route. Example setup

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_COOKIE_JAR:?Set a private cookie jar path; authenticated routes need a signed-in session}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/raids/42/share" \
  --cookie "$GUILDCORE_COOKIE_JAR" \
  --cookie-jar "$GUILDCORE_COOKIE_JAR"
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$cookieJar = getenv('GUILDCORE_COOKIE_JAR') ?: '';
if ($cookieJar === '' || !is_readable($cookieJar)) {
    throw new RuntimeException('Provide your signed-in guild cookie jar.');
}

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);
if ($cookieJar !== '') {
    curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieJar);
    curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieJar);
}

$url = $origin . '/api/raids/42/share';

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
JavaScript · guild origin
// Run in your signed-in guild portal.
const response = await fetch('/api/raids/42/share', {
  credentials: 'same-origin'
});
const result = await response.json();
if (!response.ok || !result.success) {
  throw new Error(result.message || 'Request failed');
}
console.log(result.data);
Example response 200 OK
JSON · example response
{
    "success": true,
    "data": {
        "path": "/share/<64-character share token>"
    },
    "message": null
}
POST/api/raids/{id}/share#

Publish a share link

Publish the current draft and create or reuse its share link. The draft must contain an encounter or a confirmed roster entry. This updates the plan visible to everyone with the link.

Accessraids.plan.publish + CSRFGuild hostname

Request parameters
ParameterLocation / typeRequirementDescription
idpathintegerRequiredRaid ID in the current guild.

Request examples

PHP and cURL require your signed-in guild session in a private cookie jar. A Guild API key cannot authenticate this route. Example setup

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_COOKIE_JAR:?Set a private cookie jar path; authenticated routes need a signed-in session}"

# Fetch a CSRF token using the same signed-in session. Requires jq.
session=$(curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --cookie "$GUILDCORE_COOKIE_JAR" --cookie-jar "$GUILDCORE_COOKIE_JAR" \
  "$GUILDCORE_GUILD_ORIGIN/api/session") || exit 1
csrf=$(printf '%s' "$session" | jq -er '.data.csrf | select(type == "string" and length > 0)') || exit 1

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --request POST "$GUILDCORE_GUILD_ORIGIN/api/raids/42/share" \
  --cookie "$GUILDCORE_COOKIE_JAR" \
  --cookie-jar "$GUILDCORE_COOKIE_JAR" \
  --header "X-CSRF-Token: $csrf" \
  --header 'Content-Type: application/json' \
  --data '{}'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$cookieJar = getenv('GUILDCORE_COOKIE_JAR') ?: '';
if ($cookieJar === '' || !is_readable($cookieJar)) {
    throw new RuntimeException('Provide your signed-in guild cookie jar.');
}

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);
if ($cookieJar !== '') {
    curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieJar);
    curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieJar);
}

// Read CSRF using the same cURL handle and signed-in guild session.
curl_setopt($curl, CURLOPT_URL, $origin . '/api/session');
$sessionBody = curl_exec($curl);
$sessionStatus = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
if ($sessionBody === false || $sessionStatus !== 200) {
    throw new RuntimeException('Could not read the guild session.');
}
$session = json_decode($sessionBody, true, 32, JSON_THROW_ON_ERROR);
$csrf = $session['data']['csrf'] ?? '';
if (!is_string($csrf) || $csrf === '') {
    throw new RuntimeException('The session did not return a CSRF token.');
}
$headers[] = 'X-CSRF-Token: ' . $csrf;

$url = $origin . '/api/raids/42/share';
$headers[] = 'Content-Type: application/json';
$payload = json_encode((object) [], JSON_THROW_ON_ERROR);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $payload,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
JavaScript · guild origin
// Run in your signed-in guild portal.
const sessionResponse = await fetch('/api/session', {
  credentials: 'same-origin'
});
if (!sessionResponse.ok) throw new Error('Could not read session');
const session = await sessionResponse.json();

const response = await fetch('/api/raids/42/share', {
  method: 'POST',
  credentials: 'same-origin',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': session.data.csrf
  },
  body: JSON.stringify({})
});
const result = await response.json();
if (!response.ok || !result.success) {
  throw new Error(result.message || 'Request failed');
}
console.log(result.data);
Example response 200 OK
JSON · example response
{
    "success": true,
    "data": {
        "path": "/share/<64-character share token>"
    },
    "message": null
}
DELETE/api/raids/{id}/share#

Revoke a share link

Disable the current link. Sharing again creates a new token. A revoked or unknown public token returns 404.

Accessraids.plan.publish + CSRFGuild hostname

Request parameters
ParameterLocation / typeRequirementDescription
idpathintegerRequiredRaid ID in the current guild.

Request examples

PHP and cURL require your signed-in guild session in a private cookie jar. A Guild API key cannot authenticate this route. Example setup

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"
: "${GUILDCORE_COOKIE_JAR:?Set a private cookie jar path; authenticated routes need a signed-in session}"

# Fetch a CSRF token using the same signed-in session. Requires jq.
session=$(curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --cookie "$GUILDCORE_COOKIE_JAR" --cookie-jar "$GUILDCORE_COOKIE_JAR" \
  "$GUILDCORE_GUILD_ORIGIN/api/session") || exit 1
csrf=$(printf '%s' "$session" | jq -er '.data.csrf | select(type == "string" and length > 0)') || exit 1

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --request DELETE "$GUILDCORE_GUILD_ORIGIN/api/raids/42/share" \
  --cookie "$GUILDCORE_COOKIE_JAR" \
  --cookie-jar "$GUILDCORE_COOKIE_JAR" \
  --header "X-CSRF-Token: $csrf" \
  --header 'Content-Type: application/json' \
  --data '{}'
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];
$cookieJar = getenv('GUILDCORE_COOKIE_JAR') ?: '';
if ($cookieJar === '' || !is_readable($cookieJar)) {
    throw new RuntimeException('Provide your signed-in guild cookie jar.');
}

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);
if ($cookieJar !== '') {
    curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieJar);
    curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieJar);
}

// Read CSRF using the same cURL handle and signed-in guild session.
curl_setopt($curl, CURLOPT_URL, $origin . '/api/session');
$sessionBody = curl_exec($curl);
$sessionStatus = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
if ($sessionBody === false || $sessionStatus !== 200) {
    throw new RuntimeException('Could not read the guild session.');
}
$session = json_decode($sessionBody, true, 32, JSON_THROW_ON_ERROR);
$csrf = $session['data']['csrf'] ?? '';
if (!is_string($csrf) || $csrf === '') {
    throw new RuntimeException('The session did not return a CSRF token.');
}
$headers[] = 'X-CSRF-Token: ' . $csrf;

$url = $origin . '/api/raids/42/share';
$headers[] = 'Content-Type: application/json';
$payload = json_encode((object) [], JSON_THROW_ON_ERROR);

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $payload,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
JavaScript · guild origin
// Run in your signed-in guild portal.
const sessionResponse = await fetch('/api/session', {
  credentials: 'same-origin'
});
if (!sessionResponse.ok) throw new Error('Could not read session');
const session = await sessionResponse.json();

const response = await fetch('/api/raids/42/share', {
  method: 'DELETE',
  credentials: 'same-origin',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': session.data.csrf
  },
  body: JSON.stringify({})
});
const result = await response.json();
if (!response.ok || !result.success) {
  throw new Error(result.message || 'Request failed');
}
console.log(result.data);
Example response 200 OK
JSON · example response
{
    "success": true,
    "data": null,
    "message": "Link disabled."
}
GET/api/shared/{key}#

Read a shared plan

Read the latest published composition, encounters and assignments without signing in. Raid-Helper availability reflects the latest sync. Private account IDs and editor history are excluded. Invalid, revoked or unpublished links return 404.

AccessValid share tokenGuild hostname

Request parameters
ParameterLocation / typeRequirementDescription
keypathstringRequired64 lowercase hexadecimal characters from the share link.

Request examples

cURL · terminal
: "${GUILDCORE_GUILD_ORIGIN:?Set the HTTPS base URL first}"

curl --fail-with-body --silent --show-error --proto '=https' \
  --connect-timeout 10 --max-time 30 \
  --get "$GUILDCORE_GUILD_ORIGIN/api/shared/<share-token>"
PHP · cURL extension
<?php
// PHP 8 with the cURL extension. Run from your server or local terminal.
$origin = rtrim(getenv('GUILDCORE_GUILD_ORIGIN') ?: '', '/');
if (parse_url($origin, PHP_URL_SCHEME) !== 'https') {
    throw new RuntimeException('Configure an HTTPS base URL first.');
}

$headers = ['Accept: application/json'];

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
]);

$url = $origin . '/api/shared/<share-token>';

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => $headers,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);

if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($error ?: 'API request failed: HTTP ' . $status);
}
$result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
if (($result['success'] ?? false) !== true) {
    throw new RuntimeException($result['message'] ?? 'Guild request failed.');
}

print_r($result['data']);
JavaScript · guild origin
// Run on the guild's origin. Replace <share-token> with the share token.
const response = await fetch('/api/shared/<share-token>', {
  credentials: 'same-origin'
});
const result = await response.json();
if (!response.ok || !result.success) {
  throw new Error(result.message || 'Request failed');
}
console.log(result.data);
Example response 200 OK
JSON · response excerpt
{
    "success": true,
    "data": {
        "raid": {
            "name": "Sunday Molten Core",
            "start_time": "2026-10-18 17:00:00"
        },
        "players": [
            {
                "key": "signup:7",
                "name": "Aelira",
                "class": "Priest",
                "role": "healer",
                "spec": "Holy",
                "signup_status": "confirmed",
                "current_signup_status": "confirmed",
                "signup_source": "local"
            }
        ],
        "encounters": []
    },
    "message": null
}

Responses

Errors and rate limits

Check the HTTP status for every request. Platform errors contain error and a request_id you can include when contacting support. Guild responses contain success, data and message, plus an errors object on failure.

JSON · platform error
{
    "error": "License authentication failed.",
    "request_id": "<request identifier>"
}
400 / 422
Malformed body, unsupported fields or invalid values. Correct the request before retrying.
401
Missing, invalid, revoked or expired credentials.
403
Missing permission, scope or package feature; inactive subscription; rejected origin or failed verification.
404
Unknown endpoint, unavailable resource or invalid share link.
405 / 415
Unsupported HTTP method or content type.
409
The installation binding or resource state conflicts with the request.
413
The request body exceeds the size limit.
419
Missing or invalid guild CSRF token. Read /api/session again with the same session cookie.
429
Rate limit reached. Wait for the Retry-After duration when the header is present. Otherwise, increase the delay between retries.
500 / 503
Server error or temporary service failure. Retry reads with increasing delays. Check whether a write succeeded before repeating it.

For activation and refresh retries, reuse the original request_id and request fields.

Request limits

  • Licensing: 32 KiB bodies with text fields. JSON and form-encoded bodies are accepted; unknown fields are rejected. 30 requests per license per minute, 120 per IP per minute and 2,000 globally per minute. Domain challenges also have a limit of 5 per license per 10 minutes.
  • Releases: 30 metadata requests and 10 downloads per license per hour; 90 requests per IP per minute.
  • PayPal webhooks: 256 KiB bodies; 180 requests per IP per minute and 1,000 globally per minute.
  • Guild API: 60 requests per key per minute and 120 per IP per minute, per guild.
  • Browser routes: JSON bodies up to 2 MiB unless a route sets a lower limit. Writes share a limit of 180 requests per user per minute. Shared raid reads allow 120 per IP per minute.

Your hosting infrastructure may set additional limits.