app.asktian.com — api.asktian.com Integration Guide
Version: v3.24.0 / SDK v2.0.0
Date: April 8, 2026
Audience: app.asktian.com backend and frontend engineers
API base URL: https://api.asktian.com
Table of Contents
- Quick-Start Checklist
- Authentication
- Fixing the Core Integration Bugs
- Pattern Profile — tian.global
- SSE Streaming — v3.24.0 Contract
- Decision Guidance Fan-out
- Daily Anchor Card
- Compatibility Features
- Almanac and Divination Lots
- SDK v2.0.0 Reference
- Error Handling
- Rate Limits and Caching
- Roadmap Items
1. Quick-Start Checklist
Before writing any code, complete these steps in order. Each item unblocks the next.
| Step | Action | Who |
|---|---|---|
| 1 | Receive Enterprise API key (at_live_*) from [email protected] | API team |
| 2 | Set ASKTIAN_API_KEY=at_live_xxxx in the app's server environment | App backend |
| 3 | Add Authorization: Bearer header to all API calls in asktian-api.ts | App backend |
| 4 | Fix tian.global endpoint path and parameter names (see §3) | App backend |
| 5 | Remove surname, givenName, luckyNumber from required onboarding fields | App frontend |
| 6 | Replace custom fan-out with native SSE streaming (see §5) | App backend |
| 7 | Consume done.blendedScore, done.traditionScores, done.creditsUsed from SSE done event | App frontend |
Steps 3–7 require no API changes and can be completed immediately once the key is provisioned.
2. Authentication
All API calls must include an Authorization header with a Bearer token. The token is the app's Enterprise API key. It must be stored in the server environment and never exposed to the browser.
# Correct — server-side call
curl https://api.asktian.com/api/trpc/tian.global \
-H "Authorization: Bearer at_live_xxxx" \
-G --data-urlencode 'input={"json":{"birthdate":"1983-08-03","birthTime":"22:05"}}'
// asktian-api.ts — add to every fetch call
const headers = {
"Authorization": `Bearer ${process.env.ASKTIAN_API_KEY}`,
"Content-Type": "application/json",
};
The app should use the app-level Enterprise key for all calls, with TIAN Points deducted from the app's account. Per-user billing via privyToken is on the roadmap for a future release.
3. Fixing the Core Integration Bugs
The current asktian-api.ts has three bugs that cause all API calls to fail. None require API changes — they are all frontend fixes.
Bug 1: Wrong endpoint path
The app calls https://api.asktian.com/tian.global — this is missing the /api/trpc/ prefix.
// Wrong
const url = `https://api.asktian.com/tian.global?dob=...`;
// Correct
const url = `https://api.asktian.com/api/trpc/tian.global`;
All tRPC endpoints follow the pattern /api/trpc/{procedure}. The SSE streaming endpoints follow /api/stream/tian/{tradition}.
Bug 2: Wrong parameter names
The app uses dob, tob, and city — none of these are valid parameter names.
| App uses (wrong) | API expects (correct) | Notes |
|---|---|---|
dob | birthdate | Format: YYYY-MM-DD |
tob | birthTime | Format: HH:MM (e.g. "22:05") |
city | birthPlace | Optional city string |
gender | gender | Same — "male" or "female" |
Bug 3: Missing Authorization header
Covered in §2. Without the header, all requests return HTML error pages.
Bug 4: Treating optional params as required
surname, givenName, and luckyNumber are optional on all TIAN Blended endpoints. When omitted, the Name Analysis and Auspicious Number sub-systems are skipped and the credit cost is reduced. The minimum valid call requires only birthdate.
// Minimum valid call — works for all global users
const result = await client.tian.global({
birthdate: "1983-08-03",
question: "What is my cross-civilizational pattern?",
});
// Full call for users who provide Chinese name and lucky number
const result = await client.tian.global({
birthdate: "1983-08-03",
birthTime: "22:05",
fullName: "Douglas Gan",
surname: "陈",
givenName: "志明",
luckyNumber: "88",
question: "What is my cross-civilizational pattern?",
});
4. Pattern Profile — tian.global
The tian.global endpoint is the primary call for onboarding. It runs all 29 divination systems across 5 traditions and returns a cross-civilizational Pattern Profile. This should be called once during onboarding and the result stored in the database with a 30-day TTL.
Correct call format
GET /api/trpc/tian.global?input={"json":{
"birthdate": "1983-08-03",
"birthTime": "22:05",
"fullName": "Douglas Gan",
"question": "What is my cross-civilizational pattern?"
}}
Authorization: Bearer at_live_xxxx
Response shape
The response includes all 29 tradition sub-results plus two enriched profile objects:
| Field | Type | Description |
|---|---|---|
blendedScore | number | Overall cross-civilizational score (0–100) |
synthesis | string | LLM-generated cross-tradition synthesis |
traditionScores | object | Score per tradition group |
signProfile | TianGlobalSignProfile | Western sign data: westernSign, mantra, majorArcana |
compatProfile | TianGlobalCompatProfile | Chinese compat data: chineseAnimal, partnerAnimal, loveDimensionLabel |
creditsUsed | number | TIAN Points consumed (typically 29) |
Note: The
signProfileontian.globalreturns only 3 fields (westernSign,mantra,majorArcana). For the full 9-field Western sign profile including Medical Astrology, callhoroscope.calculateseparately and use theHoroscopeSignProfileinterface from SDK v2.0.0.
Storing the Pattern Profile
// Store in pattern_profiles table after onboarding
await db.patternProfiles.upsert({
userId: user.id,
tianGlobalResult: JSON.stringify(result),
signProfile: result.signProfile,
compatProfile: result.compatProfile,
blendedScore: result.blendedScore,
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30-day TTL
});
5. SSE Streaming — v3.24.0 Contract
The native SSE streaming endpoint is the correct approach for decision guidance. The app should replace its custom fan-out (executeGuidanceFanOut) with a direct call to the SSE stream.
Endpoint
GET /api/stream/tian/global
Authorization: Bearer at_live_xxxx
Query parameters
| Parameter | Required | Description |
|---|---|---|
birthdate | Yes | User's birth date (YYYY-MM-DD) |
birthTime | No | User's birth time (HH:MM) |
question | Yes | The decision question |
fullName | No | User's full name (improves synthesis quality) |
Complete event sequence (v3.24.0)
Every tian.global stream emits events in this order:
| Event type | Emitted | Key fields | App action |
|---|---|---|---|
system | Once per tradition (up to 29×) | name, result, score, error?, tradition ✦, iconUrl ✦, systemSlug ✦ | Show tradition card with icon; start animation |
synthesis_start | Once, before first token ✦ | (no payload) | Transition to synthesis reading phase |
synthesis_chunk | One per LLM token | text | Append token to synthesis display |
done | Once, after last token | blendedScore, synthesis, traditionScores, creditsUsed, responseTimeMs, signProfile?, compatProfile? | Show final score; store reading in DB |
error | On failure | code, message | Show error toast; allow retry |
✦ New in v3.24.0
New fields on system events (v3.24.0)
The three new fields on system events eliminate the need for a separate catalogue lookup during the theatrical animation phase:
| Field | Type | Example | Use |
|---|---|---|---|
tradition | string | "eastern" | Map to tradition-specific animation |
iconUrl | string | "https://cdn.../qimen-icon.png" | Show tradition icon immediately |
systemSlug | string | "qimen" | Match against icon pack and system catalogue |
Tradition to animation mapping
tradition value | Systems included | Suggested animation |
|---|---|---|
eastern | Qimen, BaZi, Zi Wei, I Ching, Feng Shui, Mahabote, 十二宮 … | Hexagram spin / trigram reveal |
western | Tarot, Numerology, Runes, Astrology, Horoscope … | Planetary orbit / card flip |
african | Ifá, Vodun, Hakata | Shimmer pulse / cowrie scatter |
islamic | Rammal, Khatt al-Raml | Geometric pattern unfold |
indian | Jyotish, Anka Shastra | Mandala bloom |
App event type alignment
The app's internal event names differ from the API SSE event types. This table maps them directly:
| App event (internal) | API event | Notes |
|---|---|---|
tradition_start | (none) | Use synthesis_start as the phase-transition signal |
tradition_result | system | system.tradition + system.iconUrl + system.systemSlug added in v3.24.0 |
tradition_complete | (none) | Use done as the all-traditions-complete signal |
synthesis_start | synthesis_start | Direct 1:1 mapping — new in v3.24.0 |
synthesis_chunk | synthesis_chunk | Direct 1:1 mapping |
synthesis_complete | done | Full synthesis text in done.synthesis |
error | error | Direct 1:1 mapping |
TypeScript consumer (v3.24.0)
import { AskTianClient } from "asktian-sdk"; // npm install [email protected]
const client = new AskTianClient({ apiKey: process.env.ASKTIAN_API_KEY! });
for await (const chunk of client.tian.global.stream({
birthdate: user.birthdate,
birthTime: user.birthTime,
question: req.body.question,
fullName: user.fullName,
})) {
if (chunk.type === "system") {
// v3.24.0: tradition, iconUrl, systemSlug are now available
// Send to frontend via your own SSE proxy
res.write(`data: ${JSON.stringify({
type: "tradition_result",
name: chunk.name,
score: chunk.score,
tradition: chunk.tradition, // "eastern" | "western" | "african" | "islamic" | "indian"
iconUrl: chunk.iconUrl, // CDN URL for tradition icon
systemSlug: chunk.systemSlug, // e.g. "qimen", "tarot", "ifa"
})}\n\n`);
}
if (chunk.type === "synthesis_start") {
// v3.24.0: signal the frontend to transition UI phases
res.write(`data: ${JSON.stringify({ type: "synthesis_start" })}\n\n`);
}
if (chunk.type === "synthesis_chunk") {
res.write(`data: ${JSON.stringify({ type: "synthesis_chunk", text: chunk.text })}\n\n`);
}
if (chunk.type === "done") {
// Store reading in DB, then signal completion
await db.readings.create({ userId, blendedScore: chunk.blendedScore, synthesis: chunk.synthesis });
res.write(`data: ${JSON.stringify({ type: "synthesis_complete", blendedScore: chunk.blendedScore })}\n\n`);
res.end();
}
if (chunk.type === "error") {
res.write(`data: ${JSON.stringify({ type: "error", code: chunk.code, message: chunk.message })}\n\n`);
res.end();
}
}
6. Decision Guidance Fan-out
The app's current executeGuidanceFanOut function calls individual endpoints in parallel and runs its own LLM synthesis. This duplicates what the TIAN Blended SSE streaming already does natively and costs more TIAN Points.
Recommended approach: replace the fan-out with a single tian.global stream call (§5). This gives you all 29 tradition results, the LLM synthesis, and the enriched profile data in one call at a fixed cost.
If you need to call individual endpoints for specific features (e.g., showing a standalone Tarot reading), individual endpoints can be called in parallel without hitting the rate limit. A fan-out of 4–5 simultaneous calls is well within the 300 requests per 15-minute window.
Intent to endpoint mapping
For category-specific guidance, use the appropriate TIAN Blended endpoint rather than tian.global:
| Category | Recommended endpoint | Rationale |
|---|---|---|
| Career / Finance | tian.eastern | Qimen Dunjia timing, BaZi wealth pillar |
| Love / Relationships | tian.eastwest | Chinese zodiac + Western synastry |
| General / Spiritual | tian.global | All 5 traditions |
| Western-focused users | tian.western | Tarot, Numerology, Runes, Astrology |
| Indian tradition | tian.indian | Jyotish, Anka Shastra |
7. Daily Anchor Card
The Daily Pattern card is the primary daily engagement hook. The current API does not yet have a dedicated daily.anchor endpoint — this is under active evaluation for v3.24.0 API (target: April 2026). In the meantime, the app can compose the Daily Anchor from two existing calls.
Interim approach (available now)
// Call 1: Today's almanac (free, no birth data required)
const almanac = await client.almanac.daily({
date: new Date().toISOString().split("T")[0], // "2026-04-08"
});
// Call 2: User's natal chart (cache this — it doesn't change)
const horoscope = await client.horoscope.calculate({
birthdate: user.birthdate,
birthHour: user.birthHour,
});
// Compose the Daily Anchor card from both responses
const dailyAnchor = {
date: almanac.date,
twelveValue: almanac.twelveValue, // e.g. "成" (Achievement)
deity: almanac.deity, // e.g. "青龍" (Azure Dragon)
fortune: almanac.fortune, // "good_fortune" | "neutral" | "caution"
auspicious: almanac.auspiciousActivities,
inauspicious: almanac.inauspiciousActivities,
sunSign: horoscope.sunSign, // User's sun sign
mantra: horoscope.signProfile?.mantra, // Sign mantra for daily focus
};
When daily.anchor is available (v3.24.0 API)
The dedicated endpoint will return a personalised daily message, lucky/avoid hours based on the user's BaZi day master, and a dominantTheme summarising the most significant current transit — all in a single call at 3 TIAN Points.
8. Compatibility Features
Chinese zodiac compatibility — compatibility.zodiac
Returns a score, level, description, and 5-dimension YouApp data for two Chinese zodiac animals.
import { ZodiacCompatResponse } from "asktian-sdk"; // v2.0.0
const result: ZodiacCompatResponse = await client.compatibility.zodiac({
animal1: "rabbit", // User's Chinese zodiac animal
animal2: "dragon", // Partner's Chinese zodiac animal
});
// result.score — 0–100
// result.level — "excellent" | "good" | "neutral" | "challenging"
// result.description — classical Chinese compatibility description
// result.dimensions.love.label — e.g. "Positive"
// result.dimensions.love.text — full dimension description
// result.dimensions.career / wealth / health / marriage — same shape
Birthday-based compatibility — compatibility.birthday
Returns Chinese 4D dimensions and Western 4D dimensions for two birth dates. This is the recommended endpoint for the Synastry Card feature.
import { BirthdayCompatResponse } from "asktian-sdk"; // v2.0.0
const result: BirthdayCompatResponse = await client.compatibility.birthday({
birthDate1: "1983-08-03",
birthDate2: "1990-05-15",
});
// result.score — overall compatibility score
// result.romanceScore / friendshipScore / marriageScore
// result.dimensions — Chinese 4D (love/career/wealth/health/marriage)
// result.horoscopeDimensions — Western 4D (love/career/wealth/health)
Zodiac sign profile — almanac.zodiacSign
Returns the full Western sign profile (Medical Astrology, Tarot, Mantra) and the 60-element Chinese zodiac profile.
import { AlmanacZodiacResponse } from "asktian-sdk"; // v2.0.0
const result: AlmanacZodiacResponse = await client.almanac.zodiacSign({
birthDate: "1983-08-03",
});
// result.westernProfile.medical — Medical Astrology body system
// result.westernProfile.mantra — Sign mantra
// result.westernProfile.majorArcana — Tarot archetype
// result.chineseProfile — 60-element profile (personality, career, love, etc.)
9. Almanac and Divination Lots
Daily almanac — almanac.daily
Returns today's Chinese almanac data. In v3.24.0 API, a language parameter will be added to return English translations of auspiciousActivities and inauspiciousActivities.
const almanac = await client.almanac.daily({
date: "2026-04-08",
// language: "en" — available in v3.24.0 API
});
Divination lot drawing — divination.draw
The 42 lot systems with 2,901 lots are fully functional. In v3.24.0 API, a language parameter will be added for English interpretations.
const lot = await client.divination.draw({
system: "guanyin", // or any of the 42 lot systems
// language: "en" — available in v3.24.0 API
// dailySeed: user.birthdate — deterministic daily lot (under evaluation)
});
10. SDK v2.0.0 Reference
Install the latest SDK:
npm install [email protected]
New in v2.0.0 — response-shape interfaces
SDK v2.0.0 adds four typed interfaces for the full response objects of the 4 enriched endpoints. Use these to type your database storage and React props.
| Interface | Endpoint | Key fields |
|---|---|---|
HoroscopeResponse | horoscope.calculate | sunSign, moonSign, ascendant, elementProfile, placements, synthesis, signProfile?, creditsUsed |
ZodiacCompatResponse | compatibility.zodiac | score, level, description, dimensions? |
BirthdayCompatResponse | compatibility.birthday | score, romanceScore, friendshipScore, marriageScore, dimensions?, horoscopeDimensions? |
AlmanacZodiacResponse | almanac.zodiacSign | western?, chinese?, chineseProfile?, westernProfile? |
Existing interfaces (v1.7.0+)
| Interface | Used for |
|---|---|
HoroscopeSignProfile | signProfile on horoscope.calculate and almanac.zodiacSign |
ChineseZodiacProfile | chineseProfile on almanac.zodiacSign |
ZodiacDimensions | dimensions on compatibility.zodiac and compatibility.birthday |
HoroscopeDimensions | horoscopeDimensions on compatibility.birthday |
TianGlobalSignProfile | signProfile on tian.global (3-field abbreviated version) |
TianGlobalCompatProfile | compatProfile on tian.global |
TianGlobalStreamChunk | Union type for for await loop on tian.global stream |
Full TypeScript example with v2.0.0 types
import {
AskTianClient,
HoroscopeResponse,
ZodiacCompatResponse,
TianGlobalStreamChunk,
} from "asktian-sdk";
const client = new AskTianClient({ apiKey: process.env.ASKTIAN_API_KEY! });
// Typed horoscope response
const horoscope: HoroscopeResponse = await client.horoscope.calculate({
birthdate: "1983-08-03",
birthHour: 22,
});
// Typed compatibility response
const compat: ZodiacCompatResponse = await client.compatibility.zodiac({
animal1: "rabbit",
animal2: "dragon",
});
// Typed streaming
for await (const chunk of client.tian.global.stream({
birthdate: "1983-08-03",
question: "Should I change careers?",
}) as AsyncGenerator<TianGlobalStreamChunk>) {
if (chunk.type === "done") {
console.log(chunk.signProfile?.mantra); // "I Use"
console.log(chunk.compatProfile?.chineseAnimal); // "rabbit"
}
}
11. Error Handling
All API errors return a consistent JSON envelope:
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key",
"data": { "httpStatus": 401 }
}
}
Common error codes:
| Code | HTTP status | Cause | Action |
|---|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid API key | Check ASKTIAN_API_KEY env var |
FORBIDDEN | 403 | Endpoint requires higher tier | Upgrade to Enterprise for tian.global |
TOO_MANY_REQUESTS | 429 | Rate limit exceeded | Back off and retry after X-RateLimit-Reset |
INSUFFICIENT_CREDITS | 402 | Not enough TIAN Points | Top up the account |
BAD_REQUEST | 400 | Invalid parameters | Check parameter names and formats |
INTERNAL_SERVER_ERROR | 500 | API-side error | Retry with exponential backoff |
Note: In v3.24.0 API, HTML error pages on 5xx responses will be replaced with the JSON envelope above. Until then, the app should handle non-JSON responses gracefully.
import { AskTianClient, AskTianError } from "asktian-sdk";
try {
const result = await client.tian.global({ birthdate: "1983-08-03" });
} catch (err) {
if (err instanceof AskTianError) {
if (err.status === 429) {
// Rate limited — retry after the reset window
const resetAt = err.body?.data?.resetAt;
await sleep(resetAt - Date.now());
} else if (err.status === 402) {
// Insufficient credits — notify the app owner
await notifyOwner({ title: "TIAN Points low", content: err.message });
}
}
}
12. Rate Limits and Caching
Rate limit headers
All API responses include rate limit headers. The app's backend proxy should forward these to the frontend:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per 15-minute window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Recommended caching strategy
Caching is essential for keeping TIAN Points costs manageable. The following TTLs are recommended:
| Data | Cache key | TTL | Notes |
|---|---|---|---|
tian.global result | tian:global:{userId} | 30 days | Refresh on significant life events |
horoscope.calculate | horoscope:{birthdate}:{birthHour} | 90 days | Natal chart never changes |
almanac.zodiacSign | almanac:zodiac:{birthDate} | 90 days | Sign profile never changes |
almanac.daily | almanac:daily:{date} | 24 hours | One almanac per day |
compatibility.zodiac | compat:zodiac:{a1}:{a2} | 90 days | Animal pair never changes |
compatibility.birthday | compat:birthday:{d1}:{d2} | 90 days | Birth date pair never changes |
| Decision guidance stream | (do not cache) | — | Each question is unique |
13. Roadmap Items
The following features are not yet available in the API but are under active development. The app should design its data model to accommodate them when they land.
| Feature | Endpoint | Target | Notes |
|---|---|---|---|
| Daily personalised reading | daily.anchor | v3.24.0 API | Almanac + natal chart + LLM synthesis in one call (3 TIAN Points) |
| Moon phase + retrograde status | almanac.daily enhancement | v3.24.0 API | Will be added as new fields to the existing endpoint |
| English almanac translations | almanac.daily language param | v3.24.0 API | language: "en" |
| English lot interpretations | divination.draw language param | v3.24.0 API | language: "en" |
SSE synthesis_start event | tian.* stream | v3.24.0 API | Phase-transition signal |
SSE tradition, iconUrl, systemSlug on system events | tian.* stream | v3.24.0 API | Eliminates catalogue lookup during animation |
| BaZi standalone endpoint | bazi.calculate | v3.25.0 API | Four Pillars, day master, 10-year luck cycle |
| Current transits | astrology.transits | v3.25.0+ API | Natal chart cross-referenced against current ephemeris |
| Per-user billing | privyToken on TIAN Blended | TBD | App-level key recommended for launch |
api.asktian.com Backend Team — April 8, 2026
