# Prompts for AI builders

A builder writes the code faster than you do. What it does not know is two things about this particular API: where the key belongs, and why a request from the browser never arrives. Both cost an hour of debugging, and both go away with one paragraph in the prompt.

That paragraph first, then seven recipes.

## The block to paste before any recipe

Copy this into the builder **before** the recipe text. It is not about code style, it is about what otherwise does not work.

```text title="house rules"
AstroWay API house rules. Apply these to every request you generate.

1. The API key is a server secret. Never put aw_live_* or aw_test_* into client
   code, into a VITE_* or NEXT_PUBLIC_* variable, or into a fetch that runs in
   the browser. Create a server route and call the API from there.
2. The browser cannot call https://api.astroway.info/v1/* directly. Those
   responses carry no access-control-allow-origin header, so a fetch from your
   app's origin fails on CORS. Two prefixes do answer a browser and neither
   needs a key: /v1/public/* returns JSON you can use, and /v1/embed/* returns
   a finished HTML page meant for an iframe, not for fetch.
3. The auth header is exactly "X-Api-Key: <key>". Authorization: Bearer is
   rejected with 401, and so is an api_key query parameter.
4. Send the birth time zone as an IANA name: "timezone": "Europe/Kyiv". Do not
   compute a UTC offset yourself. Kyiv in May 1990 was UTC+4, not the +3 it is
   today, and a wrong offset moves every house cusp.
5. Every response is {"ok": true, "data": {...}} or {"ok": false, "error":
   {"code": "...", "message": "..."}}. Read data on success and show
   error.message to the user on failure. Do not assume a bare object.
6. Planet positions arrive as ecliptic longitude in degrees, with no sign name.
   Derive it: SIGNS[Math.floor(longitude / 30)] where SIGNS runs Aries,
   Taurus, Gemini, Cancer, Leo, Virgo, Libra, Scorpio, Sagittarius,
   Capricorn, Aquarius, Pisces. Degrees inside the sign are longitude % 30.
7. Responses carry X-Credits-Used and X-Credits-Remaining. Log both on the
   server so the cost of a feature is visible before the bill is.
8. Ask for the fields you will render. Every endpoint takes ?fields= as a
   comma-separated list of dotted paths and ?precision= as decimal places:
   POST /v1/chart?fields=planets.name,planets.longitude,houses.cusp&precision=2
   answers in 559 bytes where the full response is 32,410. Same credits, and
   the tokens are yours to pay either way.
9. Get a free key at https://api.astroway.info/dashboard/sign-up and use an
   aw_test_* sandbox key while building: it spends no credits.
```

<Aside type="caution">
Point 2 is measured, not assumed: a probe page on a foreign origin gets an answer from `/v1/public/*`, while `POST /v1/chart` is blocked by the browser. A publishable `pk_*` key does not change that, it opens only `/v1/public/*` and `/v1/embed/*`.
</Aside>

## 1. Natal chart with a wheel

Two answers to one user action: the chart data and a finished SVG.

| Endpoint | Credits |
|---|---|
| `/v1/chart` | 20 |
| `/v1/render/wheel-western` | 10 |

```text title="prompt"
Build a single-page app: a birth data form (date, time, city with latitude and
longitude, IANA time zone) that renders a natal chart.

Server route POST /api/chart does two calls to AstroWay, in parallel:
  POST https://api.astroway.info/v1/chart
  POST https://api.astroway.info/v1/render/wheel-western
Both take the same body:
  { "date": "1990-05-15", "time": "14:30:00", "timezone": "Europe/Kyiv",
    "latitude": 50.45, "longitude": 30.52 }
Both need the X-Api-Key header.

/v1/chart returns data.planets (array with name, longitude, isRetrograde),
data.houses.cusps (array of 12 longitudes) and data.aspects (array with
planet1, planet2 and a type object carrying name, symbol and color).
/v1/render/wheel-western returns data.svg as a string.

The page shows the SVG at the top, then a table of planets with sign and
degree derived from longitude, then the aspect list using type.symbol and
type.color. Mark retrograde planets with an R.

Do not call AstroWay from the browser. The page calls /api/chart only.
```

Check after generation: the SVG is inserted as markup, not through `<img src>`; the time went as an `HH:mm:ss` string; the key did not reach the bundle (a search for `aw_` in the built files should find nothing).

## 2. Compatibility of two charts

One number and the parts it is made of, with no explanatory prose. `/v1/synastry/attraction-score`, **50 credits** per pair.

```text title="prompt"
Build a compatibility page: two birth-data forms side by side, one button.

Server route POST /api/compat calls
  POST https://api.astroway.info/v1/synastry/attraction-score
with body { "chart1": {...}, "chart2": {...} }, where each chart is
  { "date", "time", "timezone", "latitude", "longitude" }.

The response has data.score (a number), data.components (the parts that made
it) and data.notes. Render score as a large number with a progress ring, then
components as labelled bars, then notes as a list.

Never show the score without the components: a single number with no reason
behind it is the thing users distrust.
```

## 3. A daily horoscope with no key at all

The one recipe that needs no server route: this endpoint answers a browser and spends no credits.

```text title="prompt"
Add a daily horoscope block to the page. Call this directly from the browser,
no key and no server route needed:

  GET https://api.astroway.info/v1/public/horoscope/daily?sign=leo&lang=en

Response: data.sign, data.date, data.period, data.horoscope (the text),
data.disclaimer and data.language. Render a sign picker with the twelve signs,
remember the choice in localStorage, and always show data.disclaimer under the
text.

The first request for a date that has not been generated yet can take a while,
so show a skeleton, not a spinner that looks stuck.
```

<Aside type="note">
The limit here is per IP, 30 requests an hour for keyless calls. Enough for a page with one horoscope block; not enough to fetch all twelve signs at once.
</Aside>

## 4. Human Design

Type, strategy, authority, profile, centres and channels in one `/v1/human-design` call, **50 credits**.

```text title="prompt"
Build a Human Design page: birth data form, then the chart summary.

Server route POST /api/hd calls
  POST https://api.astroway.info/v1/human-design
with { "date", "time", "timezone", "latitude", "longitude" }.

The response has data.type, data.strategy, data.notSelfTheme, data.authority,
data.profile, data.definition, data.cross, data.centers, and the activation
arrays data.personalityActivations and data.designActivations.

Lay it out as: type and strategy as the headline, authority and profile as two
cards, centers as a list with defined ones filled and undefined ones outlined,
activations in a collapsible table. Birth time matters here more than anywhere
else, so refuse to submit without it and say why.
```

## 5. A branded PDF report

For people who sell reports rather than show screens. `/v1/reports/natal`, **5000 credits** per document, and the link lives 24 hours.

```text title="prompt"
Build a report generator: birth data form plus a branding form (company name,
report title, accent colour), and a Generate button.

Server route POST /api/report calls
  POST https://api.astroway.info/v1/reports/natal
with
  { "chart": { "date", "time", "timezone", "latitude", "longitude" },
    "whitelabel": { "companyName": "...", "reportName": "...",
                    "themeColor": "#7c3aed", "footerText": "..." } }

The response has data.url, data.page_count, data.byte_length and
data.expires_at. The URL is valid for 24 hours only, so the server must
download the PDF immediately and store it on your side, then hand the user
your own link. Never give the AstroWay URL to the end user.

Show page_count and a download button. One report costs 5000 credits: put a
confirmation step before the call, not after.
```

The full catalogue of sixteen reports, what is in each and how long it runs: [Reports API](/en/products/reports-api/).

## 6. A month of transits

A list of events in a date window, enough for both a feed and a calendar. `/v1/transit-calendar`, **100 credits** per window.

```text title="prompt"
Build a month view of transits.

Server route POST /api/transits calls
  POST https://api.astroway.info/v1/transit-calendar
with
  { "natal": { "date", "time", "timezone", "latitude", "longitude" },
    "startDate": "2026-10-01", "endDate": "2026-10-31" }

The response has data.count and data.events, plus the echoed startDate,
endDate and maxOrb. Render events on a month grid, one dot per day with
events, and a day panel listing that day's events.

One call covers the whole window, so fetch the month once and filter in the
client. Do not call the API per day: that is thirty-one calls and thirty-one
times the credits for the same answer.
```

## 7. A tarot spread

The cheapest way to add a daily reason to come back. `/v1/tarot/rider-waite/draw/three-card`, **10 credits** per spread, or nothing for the daily card.

```text title="prompt"
Add a three-card tarot spread.

Server route POST /api/tarot calls
  POST https://api.astroway.info/v1/tarot/rider-waite/draw/three-card
with an optional { "seed": 7 }. The response has data.spread, data.seed and
data.drawn.

Pass a seed derived from the user id and today's date, so the same person gets
the same spread all day and a new one tomorrow. Without a seed every reload
deals a new hand, which reads as a slot machine.

For a zero-cost daily card the browser can call
  GET https://api.astroway.info/v1/public/tarot/daily
directly, no key.
```

## Eight builders, one difference

The recipes above are the same for all of them. The difference is only where the server route lives and where the key goes.

| Builder | Where the server call lives | Where the key goes |
|---|---|---|
| Lovable | a Supabase edge function | Supabase project secrets |
| Bolt | a server function | the Secrets panel in Bolt |
| v0 | a Next.js route handler on Vercel Functions | a Vercel environment variable, typed Secret |
| Replit | an ordinary server process | Replit Secrets, **and again separately under Deployments** |
| Cursor, Claude Code, Devin Desktop, Copilot | whatever your stack has | `.env` outside git, plus your host's own store |

**Four separate traps, each worth an evening.**

- **Lovable and Bolt build the frontend with Vite**, and Vite hands the browser any variable prefixed `VITE_`. A key does not go there even temporarily: it ends up in a bundle served to everyone.
- **Bolt previews inside a WebContainer**, which is a real browser with real CORS that cannot be switched off there. A server function is not the nicer option here, it is the only way the call happens at all.
- **v0 inlines `NEXT_PUBLIC_*` into the bundle at build time.** Changing the value in the dashboard is not enough: until there is a new deployment the app still runs on the old key. Rotating a key means rotating and rebuilding.
- **Replit keeps workspace secrets and deployment secrets apart.** What worked in the editor fails after publishing with an empty key until you add it again in the Deployments pane.

The last four rows are not platforms but agents inside your repository, and each has its own network allowlist for its sandbox. When the agent goes to test the code it just wrote, a call to `api.astroway.info` can be blocked until the domain is added. There is a shorter path for them too: connect our MCP server and the agent calls the API itself, with no code of yours in between. Instructions at [Agent setup](/en/agent-setup/).

<Aside type="note">
Windsurf has been called Devin Desktop since 2 June 2026, existing users were migrated automatically and the documentation moved to `docs.devin.ai`. Anything you find under the Windsurf name today was written before that date.
</Aside>

## If your agent speaks function calling rather than MCP

Take the ready tool definitions and hand them to the model directly. They are generated from the OpenAPI document, so the schema the model fills in is the schema the endpoint validates.

<Code lang="bash" title="terminal" code={`curl "https://api.astroway.info/v1/agent/tools?format=openai&select=starter" \\
  -H "X-Api-Key: aw_live_YOUR_KEY"`} />

`format` takes `openai` and `anthropic`; `select` narrows the catalogue to a starter set instead of every tool.
