LinkStacked

Profiles & links

The bread-and-butter API surface. Every Linkstacked profile is the top-level container, and every link belongs to exactly one profile.

Create a profile

mutation CreateProfile($input: CreateProfileInput!) {
  createProfile(input: $input) {
    profile { _id username displayName }
  }
}
{
  "input": {
    "username": "your-handle",
    "displayName": "Your Name",
    "bio": "Optional one-liner shown under the avatar."
  }
}

Usernames are globally unique and case-insensitive. Reserved keywords (/api, /dashboard, etc.) are rejected with a structured error so your client can show an inline message without parsing strings.

Most fields on Link are optional — only url and title are required.

mutation CreateLink($input: CreateLinkInput!) {
  createLink(input: $input) {
    link { _id url title icon position }
  }
}

Common variants:

  • Plain URL{ url, title } is the minimum viable link.
  • Custom icon — pass iconKey (e.g. "spotify") or iconAssetId for a user-uploaded image.
  • Scheduled — pair with the scheduling API to set a scheduleConfig.

Reorder

Profiles render links in position order, ascending. Move a link by calling reorderLinks with the desired ordered list of IDs — Linkstacked recomputes positions in a single transaction so you don't have to chain N updates:

mutation Reorder($linkIds: [ID!]!) {
  reorderLinks(linkIds: $linkIds) {
    profile { _id }
  }
}

Read a public profile

The same data the public profile page renders is exposed through publicProfileByUsername:

query Public($username: String!) {
  publicProfileByUsername(username: $username) {
    profile {
      _id displayName bio avatarUrl
      appearance { themeId font layout }
    }
    links { _id url title icon position }
  }
}

Public reads are unauthenticated — no bearer required — but rate-limited per IP. If you're building a server-side renderer, send your own X-Forwarded-For header so the bucket isn't shared with every other caller from your egress.

Custom domains and QR-coded routes

publicProfileByDomain and publicProfileByQRCodeId mirror the username route for branded surfaces — see QR & pixels for the QR side.