Branded Western Astrology text reports
Branded Western Astrology text reports
Generate customizable full-text reports and branded PDFs
POST https://api.freeastroapi.com/api/v1/natal/calculateAuthentication header: x-api-key
Use the natal endpoint with psychological_report.enabled=true to generate rich Western astrology text reports, structured markdown/JSON output, and downloadable PDFs with your profile logo in the footer.
Full report generation usually takes about 5-9 minutes. This is expected because the report is produced through a rich multi-pass pipeline: chart synthesis, evidence ranking, chapter planning, drafting, revision, and validation.
Build the payload in the pipeline editor
The custom report builder lets you configure chart inputs, chapter structure, evidence topics, tone and depth, chart styling, PDF delivery, footer labels, saved presets, and profile branding without hand-writing JSON first.
Safe retries with Idempotency-Key
Authenticated, billable astrology POST requests accept the optional header Idempotency-Key: <client-generated unique operation key>. Reuse the same key only when retrying the exact same method, path, query string, and JSON body after a timeout or network failure.
A completed replay returns the first response with Idempotency-Replayed: true, does not rerun the calculation, and does not consume extra quota. Keys are retained for about 24 hours.
Reusing a key with a changed request returns 409 idempotency_key_reused. A duplicate while the first request is still running returns 409 request_in_progress with Retry-After.
Zero-Credit Test Chart
Use this exact synthetic chart payload with the canonical demo pipeline to test the branded report flow without spending report credits. The bypass is intentionally narrow: same synthetic birth signature, standard report generation, and no custom chapter structure or custom generation instructions.
{
"name": "Synthetic Demo Candidate",
"year": 1992,
"month": 11,
"day": 23,
"hour": 14,
"minute": 17,
"city": "Lisbon",
"tz_str": "Auto",
"psychological_report": {
"enabled": true,
"mode": "async",
"style": "standard",
"output_format": "pdf",
"include_chart_in_pdf": true,
"include_profile_branding_logo": true,
"pdf_footer_label": "Acme Astrology",
"markdown": true
}
}To test a custom chapter structure, send the same birth data with a minimal report_structure. This verifies customization, but it is treated as normal report generation and consumes 1 report credit unless the exact same payload returns from cache:
{
"name": "Synthetic Demo Candidate",
"year": 1992,
"month": 11,
"day": 23,
"hour": 14,
"minute": 17,
"city": "Lisbon",
"tz_str": "Auto",
"psychological_report": {
"enabled": true,
"mode": "async",
"style": "standard",
"output_format": "pdf",
"include_chart_in_pdf": true,
"include_profile_branding_logo": true,
"pdf_footer_label": "Acme Astrology",
"markdown": true,
"report_structure": {
"chapters": [
{
"id": "orientation",
"enabled": true,
"title": "Orientation",
"question": "What core psychological pattern does this chart describe?",
"target_words": "450-650",
"evidence_topics": ["identity", "temperament", "integration"],
"subsections": [
{
"heading": "Dominant pattern",
"instructions": "Name the chart's main psychological pattern."
},
{
"heading": "Developmental direction",
"instructions": "Describe the growth task without fatalistic prediction."
}
]
}
]
}
}
}Any other birth data or substantive pipeline customization follows normal report credit billing.
Download Full Sample Outputs
These files are full outputs generated from the same synthetic demo chart, provided as fixed snapshots for integration preview.
Request Parameters
| Parameter | Type | Req | Description |
|---|---|---|---|
| psychological_report.enabled | boolean | Yes | Enable report generation. Default: false. |
| psychological_report.mode | string | No | Report delivery mode: async or sync. Default: async. |
| psychological_report.style | string | No | Report style: standard or jungian_parental. Default: standard. |
| psychological_report.markdown | boolean | No | Include report_markdown in completed responses. Default: false. |
| psychological_report.output_format | string | No | Set to pdf when you want a downloadable PDF URL. Default: json. |
| psychological_report.include_chart_in_pdf | boolean | No | Add a natal chart cover page to PDF output. Default: false. |
| psychological_report.include_profile_branding_logo | boolean | No | Apply the API-key owner's profile branding logo to PDF footers. Default: true. |
| psychological_report.pdf_footer_label | string | No | Custom text printed in the PDF footer before date and page number. |
| psychological_report.report_structure | object | No | Custom chapter plan with titles, questions, target words, evidence topics, and subsections. |
| psychological_report.tone / depth / audience | string | No | Writing controls used by the report pipeline. |
| psychological_report.focus_areas | string[] | No | High-level report priorities, such as identity, relationships, or integration. |
| psychological_report.custom_instructions | string | No | Additional generation guidance for the report pipeline. |
| psychological_report.include_natal_chart_in_response | boolean | No | Include natal chart payload in report polling response. Default: false. |
Create a branded custom report
curl -X POST "https://api.freeastroapi.com/api/v1/natal/calculate" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-H "Idempotency-Key: report-synthetic-demo-001" \
-d '{
"name": "Synthetic Demo Candidate",
"year": 1992,
"month": 11,
"day": 23,
"hour": 14,
"minute": 17,
"city": "Lisbon",
"tz_str": "Auto",
"include_fixed_stars": true,
"fixed_stars": ["Sirius", "Regulus", "Spica"],
"include_dominants": true,
"include_rulers": true,
"psychological_report": {
"enabled": true,
"mode": "async",
"style": "standard",
"output_format": "pdf",
"include_chart_in_pdf": true,
"include_profile_branding_logo": true,
"pdf_footer_label": "Acme Astrology",
"markdown": true,
"tone": "essayistic",
"depth": "deep",
"audience": "advanced astrology app user",
"focus_areas": ["identity", "relationships", "integration"],
"custom_instructions": "Prioritize precise psychological synthesis over generic placement descriptions.",
"report_structure": {
"chapters": [
{
"id": "orientation",
"enabled": true,
"title": "Orientation",
"question": "What core psychological pattern does this chart describe?",
"target_words": "450-650",
"evidence_topics": ["identity", "temperament", "integration"],
"subsections": [
{
"heading": "Dominant pattern",
"instructions": "Name the chart's main psychological pattern."
},
{
"heading": "Developmental direction",
"instructions": "Describe the growth task without fatalistic prediction."
}
]
}
]
}
}
}'Poll and download the PDF
- Create report request with
mode: "async". - Read
job_idandnext_poll_after_seconds. - Poll
GET /api/v1/natal/report/{job_id}?format=jsonusing the server cadence. - When status is
completed, download the PDF withformat=pdf. - Use
include_profile_branding_logo=falseon the PDF request if you need an unbranded copy.
{
"subject": { "...": "normal natal chart payload..." },
"credits_used": 0,
"psychological_report": {
"status": "pending",
"mode": "async",
"style": "standard",
"output_format": "pdf",
"include_chart_in_pdf": true,
"job_id": "7a7f3f9f-...",
"fetch_url": "/api/v1/natal/report/7a7f3f9f-...",
"pdf_url": "/api/v1/natal/report/7a7f3f9f-...?format=pdf&include_profile_branding_logo=true",
"credits_used": 0,
"include_profile_branding_logo": true,
"next_poll_after_seconds": 15
}
}curl -X GET "https://api.freeastroapi.com/api/v1/natal/report/{job_id}?format=json" \
-H "x-api-key: YOUR_API_KEY"{
"job_id": "7a7f3f9f-...",
"status": "completed",
"credits_used": 0,
"report_metadata": {
"report_id": "e5efe6ea-...",
"title": "Jane Example Natal Psychological Report",
"generated_at": "2026-03-21T11:44:09.531Z",
"provider": "openai-compatible",
"model": "gpt-5.3-chat-latest",
"summary": "Core report summary...",
"word_count": 7350
},
"report_markdown": "## Introduction ... (full markdown report)"
}curl -L "https://api.freeastroapi.com/api/v1/natal/report/{job_id}?format=pdf&include_profile_branding_logo=true" \
-H "x-api-key: YOUR_API_KEY" \
-o branded-western-report.pdfCredits and Cache
- This flow uses special
report credits, not normal request quota. - The published synthetic test chart is exempt only for the canonical demo/default pipeline and returns
credits_used: 0for endpoint testing. - Customizing generation substance consumes 1 report credit, even on the synthetic test chart. This includes custom
report_structure, chapter questions/subsections,custom_instructions,focus_areas, tone/depth/audience changes, or non-default report style. - Fresh report generation consumes 1 report credit (
credits_used: 1). - A cache hit for the same birth data and same report settings reuses the stored report and does not consume another credit.
- The same payload, including the same custom
report_structure, can return the same completed job/report from cache. - Use
Idempotency-Keyfor safe retries of the original POST request.
Output Format
Completed responses always include report_metadata. Output body is exclusive: with psychological_report.markdown=false (default) you receive report_content; with psychological_report.markdown=true you receive report_markdown.
Profile Branding
- The logo is stored on the user profile, not inside the report request payload.
- Upload or replace it from the Delivery panel in the custom report builder.
- PDF downloads use the logo attached to the account that owns the API key.
- Set
include_profile_branding_logo=falseon the request or PDF URL to omit it. - Logo changes are applied at PDF render time; cached report text does not need to be regenerated.
Common Mistakes
- Missing
x-api-keyheader. - Polling too fast instead of respecting
next_poll_after_seconds/Retry-After. - Providing
callback_urlwithoutcallback_signing_secret. - Forgetting that markdown is opt-in. Set
psychological_report.markdown=trueif you wantreport_markdown. - Expecting the logo to follow an API key from a different account. Branding is tied to the API-key owner profile.
Advanced: Webhook Callback (Optional)
This is optional and more advanced. If you send callback_url and callback_signing_secret, the API sends a signed callback when the job is completed or failed.
| Parameter | Type | Req | Description |
|---|---|---|---|
| psychological_report.callback_url | string | No | HTTPS callback endpoint for completed/failed report events. Default: null. |
| psychological_report.callback_signing_secret | string | No | Webhook signing secret. Required when callback_url is set. Default: null. |
{
"psychological_report": {
"enabled": true,
"mode": "async",
"output_format": "pdf",
"callback_url": "https://example.com/webhooks/freeastro/report",
"callback_signing_secret": "replace-with-a-long-random-secret"
}
}{
"event": "psychological_report.completed",
"job_id": "7a7f3f9f-...",
"status": "completed",
"credits_used": 1,
"report_metadata": { "...": "same metadata object as polling response" },
"report_markdown": "## Introduction ...",
"created_at": "2026-03-21T11:44:09.531Z"
}
Headers:
X-FreeAstro-Event: psychological_report.completed
X-FreeAstro-Timestamp: 1711025109
X-FreeAstro-Signature: <HMAC_SHA256_HEX>import crypto from "node:crypto";
import express from "express";
const app = express();
app.use(express.raw({ type: "application/json" }));
app.post("/webhooks/freeastro/report", (req, res) => {
const rawBody = req.body.toString("utf8");
const timestamp = req.header("X-FreeAstro-Timestamp") || "";
const signature = req.header("X-FreeAstro-Signature") || "";
const secret = process.env.FREE_ASTRO_CALLBACK_SECRET || "";
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send("invalid signature");
}
const event = JSON.parse(rawBody);
// Save report by event.job_id, then ack
return res.status(200).json({ ok: true });
});