Endpoints
Every v1 endpoint needs an Authorization: Bearer pk_... header except
where noted, and every list endpoint accepts the shared limit and cursor
parameters described in the Overview.
Base URL: https://demo.stellarpassport.xyz
Organizations
List organizations
GET /api/v1/organizationsReturns only organizations that have a synced Luma calendar configured — not every organization on Passport. At the time of writing this returns 2, while at least 12 organizations own events in the catalogue.
total counts that filtered set, not all organizations. There is currently
no endpoint that lists every organization.
This endpoint also ignores limit and cursor and reports a total rather
than has_more / next_cursor.
{
"total": 2,
"organizations": [
{
"id": 7,
"name": "Stellar Chile",
"slug": "stellar-chile",
"luma_url": "https://luma.com/telluscoop",
"last_synced_at": null
}
]
}Get an organization’s public page
GET /api/v1/organizations/[slug]/pageNo API key required. This is the one public endpoint, intended for embedding an organization’s page.
Returns the organization’s profile plus an ordered list of content blocks.
Each block has a type (link, text, and so on) and the fields that type
needs.
{
"slug": "stellar-nigeria",
"display_name": "Stellar Nigeria",
"tagline": "Growing the Stellar Ecosystem in Nigeria",
"cover_image_url": "https://...",
"avatar_url": "https://...",
"blocks": [
{ "id": "8cf15f46-...", "type": "text", "body": "**Stellar Nigeria** is ..." },
{ "id": "e61029f5-...", "type": "link", "label": "Register", "url": "https://...", "icon": "link", "style": "outline" }
],
"updated_at": "2026-08-16T20:09:52.651Z"
}Events
These serve the Luma-synced calendar. For events people join inside Passport, see the internal surface below.
List events
GET /api/v1/events| Parameter | Description |
|---|---|
status | upcoming, past, or all |
org_id | Restrict to one organization |
{
"events": [ ... ],
"has_more": false,
"next_cursor": null
}Upcoming events
GET /api/v1/events/upcoming
GET /api/v1/events/upcoming/[orgId]A convenience shortcut for GET /api/v1/events?status=upcoming, optionally
scoped to a single organization.
Hackathons
GET /api/v1/hackathons
GET /api/v1/hackathons/[slug]The list wraps results in hackathons; the detail endpoint returns the
hackathon object directly, with additional fields such as team_min and
team_max.
{
"hackathons": [
{
"slug": "instawards-september",
"name": "Instawards - September",
"description": null,
"image": null,
"status": "registration_open",
"format": "instawards",
"hackathon_type": "chapter",
"start_date": null,
"end_date": null,
"timezone": "UTC",
"registration_opens_at": null,
"submission_opens_at": null,
"submission_deadline": null,
"judging_deadline": null,
"results_published_at": null,
"prize_pool": null,
"participant_count": 0
}
],
"has_more": true,
"next_cursor": "2026-08-20T16:11:49.159Z|68"
}Bounties
GET /api/v1/bounties
GET /api/v1/bounties/[id]Returns open, public bounties only. Bounties that are private, closed, or still in draft are never exposed here.
{
"bounties": [],
"has_more": false,
"next_cursor": null
}Profiles
GET /api/v1/profiles
GET /api/v1/profiles/[username]Profiles are the builder surface. Note the response key is builders, not
profiles, and builders are addressed by their github_username.
{
"builders": [
{
"github_username": "samjay8",
"display_name": "Samuel Ojetunde",
"avatar_url": "https://avatars.githubusercontent.com/u/197444055?s=200",
"bio": null,
"role_title": null,
"location": null,
"website_url": null,
"twitter_handle": null,
"telegram_handle": null,
"discord_handle": null,
"stellar_address": null,
"is_featured": false,
"created_at": "2026-08-28T18:17:20.235Z",
"projects": [],
"stats": { "totalCommits30d": 0, "activeDays30d": 0, "lastActiveDate": null }
}
],
"has_more": true,
"next_cursor": "2026-08-28T22:25:17.184Z|4eff7598-..."
}The detail endpoint wraps its result in builder and adds fields the list
omits, including github_id, discord_username, scf_tier, and a
visibility object recording which fields the builder has chosen to make
public.
{
"builder": {
"github_username": "samjay8",
"scf_tier": "verified",
"visibility": { "bio": true, "wallet": true, "location": true, "projects": true }
}
}Respect visibility: a field being present in the payload does not mean the
builder wants it displayed.
Not available
These do not exist. If you have been given code that calls them, it is out of date:
| Path | Use instead |
|---|---|
GET /api/v1/builders | GET /api/v1/profiles |
GET /api/v1/events/:id/leaderboard | Not yet available on v1 |
A public leaderboard endpoint is planned but not shipped.
Internal surface
Everything below is the unversioned /api/* surface. It powers Passport’s own
front ends, including this website’s homepage. It is not versioned, has no
stability guarantee, and can change without notice. It is documented here so
that the homepage’s behaviour is explainable, not so that you build on it.
These events are a mix of Luma-synced records and events hosted on Passport.
An event with a luma_url is synced and has no page on Passport — link
it to its luma_url. An event without one is hosted on Passport and lives at
/events/<slug>.
/events/<slug> responds 200 with an identical shell whether or not the
event exists, resolving client-side, so you cannot probe validity by status
code.
List events
GET /api/events{
"events": [
{
"id": 67,
"name": "From Code to Mainnet: Shipping on Stellar",
"slug": "from-code-to-mainnet-shipping-on-stellar",
"description": "...",
"start_date": "2026-09-05 12:00:00",
"end_date": "2026-09-05 03:00:00",
"branding": {},
"image": "https://images.lumacdn.com/...",
"luma_url": "https://luma.com/5m7bx5wp",
"featured_rank": null
}
]
}featured_rank is null for ordinary events. A non-null integer marks the
event as featured on the homepage, lower values first.
Get an event
GET /api/events/[slug]By slug only — numeric ids return 404.
Leaderboard
GET /api/leaderboard| Parameter | Description |
|---|---|
metric | points (default) |
window | all or month |
limit | Requested size. The endpoint returns at most 10 entries. |
{
"metric": "points",
"window": "month",
"entries": [
{
"position": 1,
"value": 4050,
"name": "Ada Example",
"published": true,
"profileUrl": "/builder/ada",
"avatarUrl": "https://...",
"rank": 3,
"rankName": "Gold",
"rankLabel": "Gold I",
"rankInsignia": "/ranks/gold.svg?v=2"
}
]
}Builders who have not opted into public display return published: false
with a null name and a placeholder label such as "Builder #4". Their
identity fields, including all rank fields, are null. They keep their
position — drop them and the ranking develops gaps.
position uses standard competition ranking, so builders on equal points
share a position and the next distinct score skips ahead.
rankInsignia is a path on the Passport host. Prefix it with the origin and
keep the ?v= query, which is the artwork cache-buster.
Timestamps
start_date and end_date are formatted YYYY-MM-DD HH:mm:ss with no
timezone offset, and the value is the event’s local wall-clock time. Do
not treat it as UTC. Parse it consistently and avoid timezone-converting it
for display until offsets are added.