API Documentation
Everything you need to integrate with the Tampa.dev platform.
Profile & Users
Identity
GET /v1/me
Get the authenticated user's basic identity. Scope: read:user.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/me
{
"data": {
"id": "usr_abc123",
"name": "Jane Developer",
"avatarUrl": "https://avatars.githubusercontent.com/u/12345",
"username": "janedev",
"email": "jane@example.com"
}
}
The email field is only included when the token has the user:email scope.
Profile
GET /v1/profile
Get the authenticated user's full profile. Scope: read:user.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile
{
"data": {
"id": "usr_abc123",
"name": "Jane Developer",
"username": "janedev",
"avatarUrl": "https://avatars.githubusercontent.com/u/12345",
"heroImageUrl": null,
"themeColor": "#0891B2",
"bio": "Full-stack developer in Tampa",
"location": "Tampa, FL",
"socialLinks": ["https://github.com/janedev", "https://twitter.com/janedev"],
"role": "user",
"profileVisibility": "public",
"showOnLeaderboard": true,
"showMeetpassConnections": true,
"createdAt": "2025-06-15T10:30:00Z",
"email": "jane@example.com"
}
}
PATCH /v1/profile
Update the authenticated user's profile. Scope: user.
curl -X PATCH \
-H "Authorization: Bearer td_pat_abc123..." \
-H "Content-Type: application/json" \
-d '{
"bio": "Building cool stuff in Tampa",
"location": "Tampa, FL",
"themeColor": "#E85A4F"
}' \
https://api.tampa.dev/v1/profile
{
"data": {
"id": "usr_abc123",
"name": "Jane Developer",
"username": "janedev",
"bio": "Building cool stuff in Tampa",
"location": "Tampa, FL",
"themeColor": "#E85A4F"
}
}
Updatable fields:
| Field | Type | Constraints |
|---|---|---|
name | string | 1-100 characters |
username | string | 3-30 chars, alphanumeric + hyphens, lowercased |
avatarUrl | string | null | Valid URL |
heroImageUrl | string | null | Valid URL |
themeColor | string | null | One of: coral, ocean, sunset, forest, violet, rose, slate, sky |
bio | string | null | Max 500 characters |
location | string | null | Max 100 characters |
socialLinks | string[] | null | Up to 5 valid URLs |
showOnLeaderboard | boolean | Show/hide from leaderboard rankings |
showMeetpassConnections | boolean | Show/hide MeetPass connections on profile |
profileVisibility | string | public or private |
GET /v1/profile/email
Get the authenticated user's email address. Scope: user:email.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/email
{
"data": {
"email": "jane@example.com"
}
}
Entitlements
GET /v1/profile/entitlements
Get the authenticated user's active entitlements. Scope: user.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/entitlements
{
"data": [
{
"entitlement": "group_creation",
"source": "admin_grant",
"grantedAt": "2025-09-01T00:00:00Z",
"expiresAt": null
},
{
"entitlement": "badge_issuer",
"source": "admin_grant",
"grantedAt": "2025-09-01T00:00:00Z",
"expiresAt": "2026-09-01T00:00:00Z"
}
]
}
Entitlements are API-only and never exposed on public profiles.
Portfolio
GET /v1/profile/portfolio
List the authenticated user's portfolio items. Scope: read:portfolio.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/portfolio
{
"data": [
{
"id": "pf_abc123",
"userId": "usr_abc123",
"title": "My Open Source Project",
"description": "A CLI tool for developers",
"url": "https://github.com/janedev/project",
"imageUrl": null,
"sortOrder": 0,
"createdAt": "2025-10-01T12:00:00Z"
}
]
}
POST /v1/profile/portfolio
Create a portfolio item. Scope: write:portfolio.
curl -X POST \
-H "Authorization: Bearer td_pat_abc123..." \
-H "Content-Type: application/json" \
-d '{
"title": "My Project",
"url": "https://example.com",
"description": "A cool project"
}' \
https://api.tampa.dev/v1/profile/portfolio
{
"data": {
"id": "pf_def456",
"userId": "usr_abc123",
"title": "My Project",
"description": "A cool project",
"url": "https://example.com",
"imageUrl": null,
"sortOrder": 0,
"createdAt": "2025-11-15T09:00:00Z"
}
}
PATCH /v1/profile/portfolio/:id
Update a portfolio item. Scope: write:portfolio.
curl -X PATCH \
-H "Authorization: Bearer td_pat_abc123..." \
-H "Content-Type: application/json" \
-d '{"title": "Updated Title", "sortOrder": 1}' \
https://api.tampa.dev/v1/profile/portfolio/pf_def456
DELETE /v1/profile/portfolio/:id
Delete a portfolio item. Scope: write:portfolio. Returns 204 No Content.
curl -X DELETE \
-H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/portfolio/pf_def456
Achievements
GET /v1/profile/achievements
Get the authenticated user's achievement progress. Scope: read:user.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/achievements
{
"data": [
{
"key": "first_event",
"name": "First Event",
"description": "Attend your first community event",
"icon": "calendar",
"color": "#4CAF50",
"targetValue": 1,
"currentValue": 1,
"completedAt": "2025-08-20T19:00:00Z",
"badgeSlug": "first-event",
"hidden": false
},
{
"key": "social_butterfly",
"name": "Social Butterfly",
"description": "Follow 10 community members",
"icon": "users",
"color": "#2196F3",
"targetValue": 10,
"currentValue": 3,
"completedAt": null,
"badgeSlug": null,
"hidden": false
}
]
}
Badges
GET /v1/profile/badges
Get the authenticated user's earned badges with rarity information. Scope: read:user.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/badges
{
"data": [
{
"name": "Early Adopter",
"slug": "early-adopter",
"description": "Joined the Tampa.dev community early",
"icon": "🚀",
"iconUrl": "https://td-uploads-public.tampa.dev/emoji/1f680.webp",
"color": "#4CAF50",
"points": 100,
"awardedAt": "2025-06-15T10:30:00Z",
"group": null,
"rarity": {
"tier": "legendary",
"percentage": 2.5
}
},
{
"name": "First Event",
"slug": "first-event",
"description": "Attended your first community event",
"icon": "📅",
"iconUrl": "https://td-uploads-public.tampa.dev/emoji/1f4c5.webp",
"color": "#2196F3",
"points": 50,
"awardedAt": "2025-08-20T19:00:00Z",
"group": {
"id": "grp_abc123",
"name": "Tampa.dev",
"urlname": "tampadevs",
"photoUrl": "https://cdn.tampa.dev/groups/tampadevs/logo.png"
},
"rarity": {
"tier": "common",
"percentage": 45.2
}
}
]
}
Rarity tiers:
| Tier | Percentage Held |
|---|---|
legendary | < 5% |
epic | 5-10% |
rare | 10-25% |
uncommon | 25-50% |
common | > 50% |
Rarity is calculated dynamically based on the percentage of all platform users who hold each badge.
Personal Access Tokens
GET /v1/profile/tokens
List the authenticated user's PATs. Scope: user. The token value is never returned -- only the prefix.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/tokens
{
"data": [
{
"id": "tok_abc123",
"name": "My Token",
"tokenPrefix": "td_pat_a1b2",
"scopes": "read:events,read:groups",
"expiresAt": "2026-06-15T00:00:00Z",
"createdAt": "2025-12-15T10:00:00Z",
"lastUsedAt": "2026-01-30T14:22:00Z"
}
]
}
POST /v1/profile/tokens
Create a new PAT. Scope: user. The full token value is returned only in this response -- store it securely.
curl -X POST \
-H "Authorization: Bearer td_pat_abc123..." \
-H "Content-Type: application/json" \
-d '{
"name": "My Integration",
"scopes": ["read:events", "read:groups"],
"expiresInDays": 90
}' \
https://api.tampa.dev/v1/profile/tokens
{
"data": {
"id": "tok_def456",
"name": "My Integration",
"token": "td_pat_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"tokenPrefix": "td_pat_a1b2",
"scopes": "read:events,read:groups",
"expiresAt": "2026-04-30T10:00:00Z",
"createdAt": "2026-01-31T10:00:00Z"
}
}
DELETE /v1/profile/tokens/:id
Revoke a PAT. Scope: user. Returns 204 No Content.
curl -X DELETE \
-H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/tokens/tok_def456
Personal Feeds
Personal feed tokens create stable RSS and iCal URLs that automatically reflect your current favorite groups. These don't need updating when you change favorites.
GET /v1/profile/feed-token
Get (or create) a personal feed token. Scope: user.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/feed-token
{
"data": {
"feedToken": "a1b2c3d4e5f6a7b8a1b2c3d4e5f6a7b8"
}
}
Use the token to construct feed URLs:
- RSS:
https://api.tampa.dev/feed/rss/{feedToken} - iCal:
https://api.tampa.dev/feed/ical/{feedToken}
These URLs require no authentication and automatically return events from the user's current favorite groups.
POST /v1/profile/feed-token/regenerate
Regenerate the feed token, invalidating the old one. Scope: user.
curl -X POST \
-H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/profile/feed-token/regenerate
{
"data": {
"feedToken": "new1token2value3here4new1token2va"
}
}
Any existing feed URLs using the previous token will return 404.
Linked Accounts
GET /v1/me/linked-accounts
List connected OAuth providers. Scope: read:user.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/me/linked-accounts
{
"data": [
{
"provider": "github",
"providerUsername": "janedev",
"providerEmail": "jane@example.com",
"createdAt": "2025-06-15T10:30:00Z"
},
{
"provider": "google",
"providerUsername": null,
"providerEmail": "jane@gmail.com",
"createdAt": "2025-07-01T08:00:00Z"
}
]
}
Following
| Method | Endpoint | Scope | Description |
|---|---|---|---|
POST | /v1/users/:username/follow | user | Follow a user |
DELETE | /v1/users/:username/follow | user | Unfollow a user |
GET | /v1/users/:username/followers | read:user | List a user's followers |
GET | /v1/users/:username/following | read:user | List who a user follows |
POST /v1/users/:username/follow
curl -X POST \
-H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/users/johndoe/follow
New follow (201 Created):
{
"data": {
"following": true
}
}
Already following (200 OK):
{
"data": {
"alreadyFollowing": true
}
}
GET /v1/users/:username/followers
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/users/janedev/followers
{
"data": [
{
"username": "johndoe",
"name": "John Doe",
"avatarUrl": "https://avatars.githubusercontent.com/u/67890",
"followedAt": "2025-12-01T15:00:00Z"
}
],
"pagination": {
"total": 1,
"limit": 20,
"offset": 0,
"hasMore": false
}
}
GET /v1/users/:username/following
List who a user follows. Scope: read:user.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/users/janedev/following?limit=10&offset=0
{
"data": [
{
"username": "johndoe",
"name": "John Doe",
"avatarUrl": "https://avatars.githubusercontent.com/u/67890",
"followedAt": "2025-11-15T10:30:00Z"
}
],
"pagination": {
"total": 1,
"limit": 10,
"offset": 0,
"hasMore": false
}
}
Returns 404 if the user does not exist or has a private profile.
Uploads
POST /v1/uploads/request
Request a presigned upload URL for direct upload to R2 storage. Scope: user.
curl -X POST \
-H "Authorization: Bearer td_pat_abc123..." \
-H "Content-Type: application/json" \
-d '{
"category": "avatar",
"filename": "avatar.png",
"contentType": "image/png",
"size": 12345
}' \
https://api.tampa.dev/v1/uploads/request
{
"data": {
"uploadUrl": "https://uploads.tampa.dev/...",
"publicUrl": "https://cdn.tampa.dev/avatars/usr_abc123/avatar.png"
}
}
Allowed content types: image/jpeg, image/png, image/webp, image/gif. Maximum file size: 5MB.
Onboarding
| Method | Endpoint | Scope | Description |
|---|---|---|---|
GET | /v1/me/onboarding | user | Get onboarding steps with completion status |
POST | /v1/me/onboarding/:stepKey/dismiss | user | Dismiss an onboarding step |
POST | /v1/me/onboarding/dismiss-all | user | Dismiss all onboarding steps |
Onboarding steps are automatically completed when corresponding domain events fire (e.g., setting your username, joining a group, earning XP).
Public Users
GET /v1/users
List users with public profiles. Scope: read:user.
curl -H "Authorization: Bearer td_pat_abc123..." \
"https://api.tampa.dev/v1/users?search=jane&limit=10"
Query parameters:
| Parameter | Type | Description |
|---|---|---|
search | string | Filter by name or username |
badge | string | Comma-separated badge slugs (user must hold ALL) |
limit | integer | Max 100, default 20 |
offset | integer | Pagination offset |
{
"data": [
{
"username": "janedev",
"name": "Jane Developer",
"bio": "Full-stack dev in Tampa",
"location": "Tampa, FL",
"avatarUrl": "https://avatars.githubusercontent.com/u/12345",
"themeColor": "#E85A4F",
"badges": [
{ "name": "First Event", "slug": "first-event", "icon": "🎉", "color": "#FF6B6B", "description": "Attended your first event" }
],
"memberSince": "2025-01-15T00:00:00Z"
}
],
"pagination": { "total": 42, "limit": 10, "offset": 0, "hasMore": true }
}
Never exposes user id, email, or role. Only users with profileVisibility='public' and a username set are returned.
GET /v1/users/:username
Get a user's public profile. Scope: read:user.
curl -H "Authorization: Bearer td_pat_abc123..." \
https://api.tampa.dev/v1/users/janedev
Returns badges, achievements, favorite groups, portfolio items, XP score, and recent MeetPass connections (if enabled). The response includes a recentConnections array (up to 5 most recent) when the user has showMeetpassConnections enabled.
GET /v1/users/:username/connections
Get a user's MeetPass connections (paginated). Scope: read:user. Only available if the user has showMeetpassConnections enabled. The profile owner and platform admins can always view connections regardless of the visibility setting.
curl -H "Authorization: Bearer td_pat_abc123..." \
"https://api.tampa.dev/v1/users/janedev/connections?limit=20&offset=0"
{
"data": [
{
"username": "johndoe",
"name": "John Doe",
"avatarUrl": "https://avatars.githubusercontent.com/u/67890",
"connectedAt": "2026-03-08T14:00:00.000Z"
}
],
"pagination": {
"total": 1,
"limit": 20,
"offset": 0,
"hasMore": false
}
}
Returns 404 if the user doesn't exist, has a private profile, or has connections hidden.