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=jsonPartner 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.
403— App not yet approved, or key revoked.401— Missing or invalid x-api-key.429— Rate 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|pinnedResponse 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 consoleOAuth 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. Send the user to the authorization URL with your PKCE challenge and requested scopes.
- 2. The user reviews and approves on Afterlog; we redirect back to your redirect_uri with a one-time code and your state.
- 3. Exchange the code for tokens at the token endpoint, sending your original code_verifier.
- 4. Call the Partner API with Authorization: Bearer <access_token>. Access tokens last 1 hour; refresh to rotate.
Scopes
journeys.read | Read the connected user's journeys and logs, including private ones. |
profile.read | Read 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=S256Token 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.created | A connected user added a log (Update) to a journey. |
journey.published | A 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.