AFTERLOG

Afterlog for developers

Bring Afterlog journeys into your apps and services.

Embed widgets

Drop a live Before↔Current slider or journey card onto any site. Public journeys only — no API key needed.

Live preview

Script tag

Add the SDK and a placeholder div; the widget mounts and resizes automatically.

<script async src="https://afterlog.me/sdk/afterlog.js"></script>
<div data-afterlog-journey="JOURNEY_ID" data-afterlog-theme="light"></div>

Direct iframe

Embed without JavaScript. Use theme=light|dark and variant=slider|card.

<iframe
  src="https://afterlog.me/embed/journey/JOURNEY_ID?theme=light&variant=slider"
  width="480" height="520" style="border:0;max-width:100%"
  scrolling="no" loading="lazy" title="Afterlog journey"></iframe>

oEmbed

Paste a journey URL into Slack, WordPress, and other tools that support oEmbed.

GET https://afterlog.me/api/oembed?url=https://afterlog.me/journey/JOURNEY_ID&format=json

Partner API

A read-only REST API for approved partners. Pull public journeys, trending, search, and profiles into your own product.

Authentication

Send your key in the x-api-key header. Issue keys from the developer console below.

curl https://api.afterlog.me/v1/partner/trending \
  -H "x-api-key: ak_live_..."

Early access

The Partner API is invite-only for now. Register an app, then we review it before your keys go live.

  • 403App not yet approved, or key revoked.
  • 401Missing or invalid x-api-key.
  • 429Rate limit exceeded — wait for the Retry-After header, then retry.

Endpoints

All paths are relative to https://api.afterlog.me/v1 and read-only. Trending and search paginate with cursor and nextCursor.

GET /partner/trending?category=&sort=trending|helpful&cursor=&limit=
GET /partner/search?q=&cursor=&limit=
GET /partner/journeys/:id
GET /partner/profiles/:username
GET /partner/profiles/:username/journeys?sort=recent|pinned

Response shape

A journey is returned as its public view — cover, tags, stats, and owner. No viewer-specific or private fields.

{
  "items": [
    {
      "id": "cmr576hhy000n5s6a1b2c3d4",
      "userId": "cmr4qx8p0000ab12cd34",
      "title": "코 성형 회복 여정",
      "category": "BEAUTY",
      "categoryCustom": null,
      "startDate": "2026-01-15",
      "privacy": "PUBLIC",
      "status": "PUBLISHED",
      "description": "붓기와 회복 과정을 매주 기록합니다.",
      "tags": ["recovery", "beauty"],
      "coverMediaId": "cmr58a4k0001...",
      "coverUrl": "https://disk.afterlog.me/media/.../320.webp",
      "captureSlots": [{ "key": "front", "label": "Front" }, { "key": "side", "label": "Side" }],
      "heroSlot": "front",
      "createdAt": "2026-01-15T09:12:00.000Z",
      "updatedAt": "2026-07-14T02:31:00.000Z",
      "stats": {
        "daysCount": 180, "updatesCount": 12, "viewsCount": 3400,
        "savesCount": 210, "followersCount": 95, "helpfulCount": 88,
        "completedReadsCount": 40, "forksCount": 3, "helpfulnessScore": 0.82
      },
      "owner": {
        "userId": "cmr4qx8p0000ab12cd34", "username": "riah_kim",
        "displayName": "Riah", "avatarMediaId": null,
        "avatarUrl": "https://disk.afterlog.me/media/.../320.webp"
      },
      "trendingScore": 128.4
    }
  ],
  "nextCursor": "eyJjIjoiMjAyNi0wNy0xNCJ9"
}

Ready to build? Register an app and issue your first key.

Open developer console

OAuth for user data

Let users connect their Afterlog account so your app can read their journeys — including private ones — on their behalf. Authorization Code + PKCE (S256).

Flow

  1. 1. Send the user to the authorization URL with your PKCE challenge and requested scopes.
  2. 2. The user reviews and approves on Afterlog; we redirect back to your redirect_uri with a one-time code and your state.
  3. 3. Exchange the code for tokens at the token endpoint, sending your original code_verifier.
  4. 4. Call the Partner API with Authorization: Bearer <access_token>. Access tokens last 1 hour; refresh to rotate.

Scopes

journeys.readRead the connected user's journeys and logs, including private ones.
profile.readRead the connected user's public profile.

Authorization URL

Redirect the browser here. The redirect_uri must exactly match one registered on your app. PKCE with method=S256 is required.

https://afterlog.me/oauth/authorize?response_type=code
  &client_id=YOUR_APP_ID
  &redirect_uri=https://your.app/callback
  &scope=journeys.read%20profile.read
  &state=RANDOM_CSRF
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256

Token exchange

Exchange the code for an access + refresh token. Confidential clients also send client_secret.

curl -X POST https://api.afterlog.me/v1/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "client_id": "YOUR_APP_ID",
    "code": "<code from redirect>",
    "redirect_uri": "https://your.app/callback",
    "code_verifier": "<original PKCE verifier>"
  }'

Using the token

Send the access token as a Bearer header on Partner API reads. With journeys.read, the connected user's private journeys are included.

curl https://api.afterlog.me/v1/partner/journeys/JOURNEY_ID \
  -H "Authorization: Bearer <access_token>"

Webhooks

Get notified when a connected user adds a log or publishes a journey. Payloads are thin signals — fetch details from the Partner API.

Managing endpoints

Register, list, and delete endpoints per app in the developer console. The signing secret is shown once on creation. URLs must be https and must not point at private hosts.

Events

journey.update.createdA connected user added a log (Update) to a journey.
journey.publishedA connected user published a journey.

Payload

Every delivery has a unique id (use it for idempotency), an event, a timestamp, and a thin data object.

{
  "id": "whd_5f3a1c...",                     // 배달 고유 id (수신측 idempotency 키)
  "event": "journey.update.created",
  "createdAt": "2026-07-19T09:00:00.000Z",
  "data": {
    "journeyId": "jr_...", "updateId": "up_...",
    "username": "riah", "title": "코 성형 회복 여정"
  }
}

Verifying the signature

Each request carries X-Afterlog-Signature: t=<unix>,v1=<hex>, where v1 = HMAC-SHA256 of "<t>.<raw body>". Recompute over the raw body and compare in constant time.

// header: X-Afterlog-Signature: t=<unix>,v1=<hex>
const [t, v1] = header.split(',').map((s) => s.split('=')[1]);
const expected = crypto
  .createHmac('sha256', secret)                // secret = whsec_...
  .update(`${t}.${rawBody}`)                   // 받은 원문 바디 그대로 사용
  .digest('hex');
const ok = crypto.timingSafeEqual(
  Buffer.from(expected), Buffer.from(v1),
);
// t 가 최근(±5분)인지도 확인해 재전송(replay)을 막는다.

Retries and auto-disable

A 2xx is success. Failures and timeouts (10s) are retried up to 3 times. After 10 cumulative failures the endpoint is disabled automatically; re-create it to re-enable.

Afterlog for developers | Afterlog