Skip to content

API-first integrations

A valid request sample and an endpoint list instead of a partner wall.

SaaSintermediateintegrationsapiendpointsdeveloper

Live preview

full widthLive preview — open it in a new tab for the full-height version.
Open the preview in a new tab

Source

This exact file renders the preview above.

import { ArrowRight, Webhook } from 'lucide-react'
import { ButtonLink } from '@/components/ui/button'
import { Container } from '@/components/ui/layout'
import { Badge } from '@/components/ui/badge'

/**
 * API-first integration section
 *
 * For products whose real integration story is "there is an API". A short,
 * valid request sample is more convincing than a wall of partner logos, and
 * the endpoint list tells an evaluator the shape of the surface in ten seconds.
 */
const endpoints = [
  { method: 'GET', path: '/v1/components', description: 'List catalogue items with filters' },
  {
    method: 'GET',
    path: '/v1/components/{slug}',
    description: 'One item with source and relations',
  },
  { method: 'POST', path: '/v1/exports', description: 'Generate a token export' },
  { method: 'GET', path: '/v1/events', description: 'Server-sent stream of catalogue changes' },
]

const sample = `curl https://api.foundry.example/v1/components \\
  -H "Authorization: Bearer $FOUNDRY_TOKEN" \\
  -G --data-urlencode "category=overlay"`

export default function ApiFirstIntegrations() {
  return (
    <section className="border-b border-line bg-canvas py-section">
      <Container>
        <div className="grid gap-10 lg:grid-cols-2 lg:gap-16">
          <div>
            <Badge tone="accent" icon={<Webhook className="size-3" />}>
              API first
            </Badge>
            <h2 className="display-type mt-4 text-2xl font-semibold text-ink-strong sm:text-3xl">
              Everything in the catalogue is addressable.
            </h2>
            <p className="mt-4 max-w-lg text-md text-ink-muted">
              A fictional REST surface, documented the same way the components are. Tokens, sources
              and relations are all queryable.
            </p>

            <div className="mt-7 overflow-hidden rounded-xl border border-line bg-code-surface">
              <div className="border-b border-white/10 px-4 py-2">
                <span className="font-mono text-2xs text-white/50">Request</span>
              </div>
              <pre className="thin-scrollbar overflow-x-auto p-4 font-mono text-xs leading-relaxed text-code-ink">
                <code>{sample}</code>
              </pre>
            </div>

            <ButtonLink
              href="/starters/documentation/preview/api"
              variant="outline"
              className="mt-6"
              trailingIcon={<ArrowRight className="size-4" />}
            >
              Read the API reference
            </ButtonLink>
          </div>

          <div>
            <h3 className="label-caps text-ink-subtle">Endpoints</h3>
            <ul className="mt-4 divide-y divide-[var(--color-border-subtle)] rounded-xl border border-line bg-surface">
              {endpoints.map((endpoint) => (
                <li
                  key={endpoint.path}
                  className="flex flex-wrap items-baseline gap-x-3 gap-y-1 p-4"
                >
                  <Badge
                    size="sm"
                    appearance="solid"
                    tone={endpoint.method === 'GET' ? 'info' : 'success'}
                    className="font-mono"
                  >
                    {endpoint.method}
                  </Badge>
                  <code className="min-w-0 font-mono text-xs break-token text-ink-strong">
                    {endpoint.path}
                  </code>
                  <span className="w-full text-xs text-ink-muted">{endpoint.description}</span>
                </li>
              ))}
            </ul>
          </div>
        </div>
      </Container>
    </section>
  )
}

components/blocks/sections/integrations/api-first.tsx

import { ArrowRight, Webhook } from 'lucide-react'
import { ButtonLink } from '@/components/ui/button'
import { Container } from '@/components/ui/layout'
import { Badge } from '@/components/ui/badge'

/**
 * API-first integration section
 *
 * For products whose real integration story is "there is an API". A short,
 * valid request sample is more convincing than a wall of partner logos, and
 * the endpoint list tells an evaluator the shape of the surface in ten seconds.
 */
const endpoints = [
  { method: 'GET', path: '/v1/components', description: 'List catalogue items with filters' },
  {
    method: 'GET',
    path: '/v1/components/{slug}',
    description: 'One item with source and relations',
  },
  { method: 'POST', path: '/v1/exports', description: 'Generate a token export' },
  { method: 'GET', path: '/v1/events', description: 'Server-sent stream of catalogue changes' },
]

const sample = `curl https://api.foundry.example/v1/components \\
  -H "Authorization: Bearer $FOUNDRY_TOKEN" \\
  -G --data-urlencode "category=overlay"`

export default function ApiFirstIntegrations() {
  return (
    <section className="border-b border-line bg-canvas py-section">
      <Container>
        <div className="grid gap-10 lg:grid-cols-2 lg:gap-16">
          <div>
            <Badge tone="accent" icon={<Webhook className="size-3" />}>
              API first
            </Badge>
            <h2 className="display-type mt-4 text-2xl font-semibold text-ink-strong sm:text-3xl">
              Everything in the catalogue is addressable.
            </h2>
            <p className="mt-4 max-w-lg text-md text-ink-muted">
              A fictional REST surface, documented the same way the components are. Tokens, sources
              and relations are all queryable.
            </p>

            <div className="mt-7 overflow-hidden rounded-xl border border-line bg-code-surface">
              <div className="border-b border-white/10 px-4 py-2">
                <span className="font-mono text-2xs text-white/50">Request</span>
              </div>
              <pre className="thin-scrollbar overflow-x-auto p-4 font-mono text-xs leading-relaxed text-code-ink">
                <code>{sample}</code>
              </pre>
            </div>

            <ButtonLink
              href="/starters/documentation/preview/api"
              variant="outline"
              className="mt-6"
              trailingIcon={<ArrowRight className="size-4" />}
            >
              Read the API reference
            </ButtonLink>
          </div>

          <div>
            <h3 className="label-caps text-ink-subtle">Endpoints</h3>
            <ul className="mt-4 divide-y divide-[var(--color-border-subtle)] rounded-xl border border-line bg-surface">
              {endpoints.map((endpoint) => (
                <li
                  key={endpoint.path}
                  className="flex flex-wrap items-baseline gap-x-3 gap-y-1 p-4"
                >
                  <Badge
                    size="sm"
                    appearance="solid"
                    tone={endpoint.method === 'GET' ? 'info' : 'success'}
                    className="font-mono"
                  >
                    {endpoint.method}
                  </Badge>
                  <code className="min-w-0 font-mono text-xs break-token text-ink-strong">
                    {endpoint.path}
                  </code>
                  <span className="w-full text-xs text-ink-muted">{endpoint.description}</span>
                </li>
              ))}
            </ul>
          </div>
        </div>
      </Container>
    </section>
  )
}

components/ui/badge.tsx

import type { HTMLAttributes, ReactNode } from 'react'
import { variants } from '@/lib/variants'

/**
 * Badge
 *
 * A compact, non-interactive label. Tones map to the status token trios, and
 * because a badge is often the only signal in a dense table, the `dot` option
 * exists to add a second, colour-independent cue alongside the text.
 */
const badgeVariants = variants('inline-flex items-center gap-1.5 whitespace-nowrap font-medium', {
  variants: {
    tone: {
      neutral: 'bg-surface-sunken text-ink-muted border-line',
      accent: 'bg-accent-soft text-accent-soft-ink border-accent-line',
      success: 'bg-success-soft text-success border-success-line',
      warning: 'bg-warning-soft text-warning border-warning-line',
      danger: 'bg-danger-soft text-danger border-danger-line',
      info: 'bg-info-soft text-info border-info-line',
      inverse: 'bg-surface-inverse text-ink-inverse border-transparent',
    },
    appearance: {
      soft: 'border',
      outline: 'border bg-transparent',
      solid: 'border border-transparent',
    },
    size: {
      sm: 'h-4.5 rounded-sm px-1.5 text-2xs',
      md: 'h-5.5 rounded-sm px-2 text-xs',
    },
  },
  defaultVariants: { tone: 'neutral', appearance: 'soft', size: 'md' },
  compound: [
    { appearance: 'solid', tone: 'accent', class: 'bg-accent text-accent-ink' },
    { appearance: 'solid', tone: 'success', class: 'bg-success text-white' },
    { appearance: 'solid', tone: 'warning', class: 'bg-warning text-white' },
    { appearance: 'solid', tone: 'danger', class: 'bg-danger text-white' },
    { appearance: 'solid', tone: 'info', class: 'bg-info text-white' },
    { appearance: 'solid', tone: 'neutral', class: 'bg-ink text-ink-inverse' },
    { appearance: 'outline', tone: 'neutral', class: 'text-ink-muted' },
  ],
})

export type BadgeTone = 'neutral' | 'accent' | 'success' | 'warning' | 'danger' | 'info' | 'inverse'

export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
  tone?: BadgeTone
  appearance?: 'soft' | 'outline' | 'solid'
  size?: 'sm' | 'md'
  /** Adds a leading dot so the badge does not rely on hue alone. */
  dot?: boolean
  icon?: ReactNode
}

export function Badge({
  tone = 'neutral',
  appearance = 'soft',
  size = 'md',
  dot = false,
  icon,
  className,
  children,
  ...props
}: BadgeProps) {
  return (
    <span className={badgeVariants({ tone, appearance, size, className })} {...props}>
      {dot ? (
        <span className="size-1.5 shrink-0 rounded-full bg-current" aria-hidden="true" />
      ) : null}
      {icon}
      {children}
    </span>
  )
}

Demo source — adapt to your project. Foundry is not published as a package.

Usage

For products whose real integration story is 'there is an API'. A short, valid request is more convincing than a wall of partner logos.

  • The sample must actually be runnable in shape, even against a fictional host.
  • Show the response shape or link to it; an endpoint list alone is half the answer.

Variants and states

Every entry below is a genuine difference in behaviour or layout, and every one of them is visible in the preview above.

  • Request sample
  • Four endpoints
  • Method badges

Accessibility

Selectable code
The sample is a pre element, not an image.
Method badges
HTTP methods are text in a badge, readable by assistive tech.

Foundry implements published ARIA patterns and is tested against them. No WCAG certification is claimed — see the accessibility documentation for what is and is not covered.