Developer OAuth and Manatan Tracker

Manatan exposes an OAuth 2.0 authorization-code flow with S256 PKCE. It lets a user sign in to a third-party app with their Manatan account and intentionally grant scoped access to their Manatan Tracker lists.

Applications are public clients. Manatan does not issue client secrets, so every authorization request must use PKCE.

Register an application

  1. Sign in to Manatan.
  2. Open Account → Apps & API.
  3. Register your application and every redirect URI it may use. You can register up to five applications per Manatan account.
  4. Copy the generated client ID.

Redirect URIs are matched exactly. Use HTTPS for web callbacks. Native apps may use a reverse-domain custom scheme or an HTTP loopback callback on localhost, 127.0.0.1, or ::1.

Registration asks for the application name and type, a public description, redirect URLs, homepage, optional logo and policy URLs, commercial status, developer or company name, and purpose of use. These details help users identify who operates an application before granting access. Manatan shows the application status and generates its client ID after registration.

Scopes

ScopeAccess
profileRead the user's Manatan ID, username, and avatar.
tracker:readSearch the catalog and read the user's Video, Manga, and Novel tracker entries.
tracker:writeCreate, update, and delete the user's tracker entries.

Ask only for scopes your app currently needs. Users see every requested permission on Manatan's consent screen and can revoke an app later from Account → Apps & API.

Start authorization

Generate a cryptographically random state value and a PKCE verifier containing 43–128 unreserved characters. Send the base64url-encoded SHA-256 digest of that verifier as code_challenge.

GET https://manatan.com/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fexample.com%2Foauth%2Fcallback
  &state=RANDOM_STATE
  &code_challenge=BASE64URL_SHA256_VERIFIER
  &code_challenge_method=S256
  &scope=profile%20tracker%3Aread%20tracker%3Awrite

After the user signs in and approves access, Manatan redirects to the registered URI:

https://example.com/oauth/callback?code=ONE_TIME_CODE&state=RANDOM_STATE

Reject the callback if state does not exactly match the value your app stored.

Exchange the code

Send URL-encoded form data. Authorization codes are one-time use and expire after five minutes.

POST /oauth/token HTTP/1.1
Host: manatan.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
client_id=YOUR_CLIENT_ID&
code=ONE_TIME_CODE&
redirect_uri=https%3A%2F%2Fexample.com%2Foauth%2Fcallback&
code_verifier=ORIGINAL_PKCE_VERIFIER

The response contains a bearer access token, rotating refresh token, expiration, and granted scope:

{
  "access_token": "...",
  "refresh_token": "...",
  "token_type": "Bearer",
  "expires_in": 900,
  "scope": "profile tracker:read tracker:write"
}

Access tokens last 15 minutes. Refresh tokens last 30 days and rotate on every successful refresh. Store them using the secure credential storage provided by the operating system; do not place tokens in URLs or logs.

Refresh and revoke

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&client_id=YOUR_CLIENT_ID&refresh_token=REFRESH_TOKEN
POST /oauth/revoke
Content-Type: application/x-www-form-urlencoded

client_id=YOUR_CLIENT_ID&token=ACCESS_OR_REFRESH_TOKEN

Revocation is intentionally idempotent and returns success even when a token is already invalid.

Read the signed-in profile

GET /api/oauth/userinfo
Authorization: Bearer ACCESS_TOKEN
{
  "sub": "manatan-user-id",
  "aud": "your-client-id",
  "username": "reader",
  "avatar_url": "https://..."
}

This is an OAuth user-info endpoint, not an OpenID Connect ID token.

Manatan Tracker API

All tracker requests use Authorization: Bearer ACCESS_TOKEN and strict JSON requests and responses. Tracker media types are video, manga, novel, and game.

MethodEndpointScope
GET/api/oauth/tracker/search?query=title&media_type=video&limit=20tracker:read
GET/api/oauth/tracker/entries?media_type=mangatracker:read
POST/api/oauth/tracker/entriestracker:write
GET/api/oauth/tracker/entries/{entry_id}tracker:read
PATCH/api/oauth/tracker/entries/{entry_id}tracker:write
DELETE/api/oauth/tracker/entries/{entry_id}tracker:write

Create or upsert an entry with a canonical Manatan catalog ID:

{
  "media_item_id": "catalog-item-id",
  "status": "current",
  "progress_current": 3,
  "score": 85,
  "is_private": false
}

The server returns both the list-entry id and its media_item_id. Store them separately. Later updates address the list entry by its id and contain tracking fields only:

PATCH /api/oauth/tracker/entries/LIST_ENTRY_ID
Content-Type: application/json
Authorization: Bearer ACCESS_TOKEN
{
  "status": "completed",
  "progress_current": 12,
  "score": 90
}

The external schema deliberately does not accept title, native_title, cover_url, format, media_type, or progress_total. Those values come from the canonical catalog. It also does not accept media_item_id on PATCH, because a list entry cannot be reassigned to another work. Unknown fields and malformed JSON return a structured 400 invalid_request response instead of being ignored, and there is no title-based or legacy detached-entry fallback.

Supported status values are current, completed, paused, dropped, planning, and repeating. Progress, repeat count, immersion seconds, and timestamps cannot be negative. A score is an integer from 0–100; use null to remove it. Invalid values return 400 invalid_tracker_value instead of being silently clamped. Search the catalog first and preserve the returned media_item_id so Manatan can keep Video, Manga, Novel, and Game entries correctly categorized. See How list tracking works for the identity and unmatched-import rules.

List creation errors include stable media_item_required and media_not_found codes. Attempts to send catalog identity fields through the strict update schema are rejected before a mutation runs.

OAuth errors use the standard error and error_description fields. A 401 invalid_token response means the access token should be refreshed. A 403 insufficient_scope response means the user must authorize the missing scope.

Security checklist

  • Generate and verify a new random state for every request.
  • Generate a new S256 PKCE verifier for every request.
  • Use an external user-agent or system browser for native-app authorization.
  • Match your callback URI exactly to a registered redirect URI.
  • Keep access and refresh tokens out of source code, URLs, analytics, and logs.
  • Revoke tokens when the user disconnects Manatan from your app.

The flow follows OAuth 2.0, PKCE, and the OAuth 2.0 Security Best Current Practice.