A postcard from orbit: n8n, the ISS, a real NASA satellite image and an AI
Try it first: open the orbit postcard and press the button. A few seconds later a card lands in front of you, sent from the International Space Station, from exactly the spot it is flying over at that moment. The card s
Try it first: open the orbit postcard and press the button. A few seconds later a card lands in front of you, sent from the International Space Station, from exactly the spot it is flying over at that moment.
The card shows the real satellite image of the area below the ISS, the place name, altitude, speed, the local time down there and two to four sentences of greetings that an AI wrote in your language. Every card has its own link to share, and the page has a wall with the latest cards from all visitors.
What happens behind it
The work is done by an n8n workflow on my server, seven nodes from left to right. If n8n is new to you, my introduction to n8n covers the basics.
- Besucher-Knopf (Webhook): the button first calls PHP on my site. It checks the limits, creates a random card id and calls the webhook with that id and your language.
-
Wo ist die ISS?: an HTTP GET to
api.wheretheiss.at/v1/satellites/25544. It returns latitude, longitude, altitude (about 420 km), velocity (about 27,600 km/h) and whether the station is in sunlight or in Earth's shadow. - Was liegt darunter?: reverse geocoding with OpenStreetMap Nominatim, zoom level 5 (region and country), in your language.
- Postkarte schreiben: a Code node. It collects the facts and builds the prompt with clear rules.
-
KI schreibt (IONOS): an OpenAI-compatible language model (gpt-oss-120b, hosted by IONOS in Germany) answers with JSON:
{greeting, text}. - Antwort prΓΌfen: a second Code node. If the answer is empty or broken, it falls back to a template card. So you always get a card.
- An die Seite senden: an HTTP POST with a secret token to my site. There PHP downloads the satellite image, stores card and image and pins the card to the wall.
The node names are German because the workflow is the same one I show on the German page; the logic does not care.
The interesting bits
The image is a composite. It comes from NASA GIBS (Global Imagery Browse Services): the true-colour image from the MODIS instrument on the Terra satellite. I use yesterday's image, because today's only fills up over the course of the day. Even yesterday's has gaps: Terra scans the Earth in strips, and near the equator wedges without data remain between two passes. So NASA Blue Marble, a cloud-free mosaic, sits underneath. Where MODIS saw something, you see yesterday's clouds; where it did not, you see the Earth without clouds. The area is about 12Β° Γ 8Β°, roughly 1,300 Γ 900 km, centred on the ISS position.
The code names the ocean. The ISS circles the Earth about every 92 minutes, and most of the time there is water below it. Nominatim finds nothing there. So the Code node assigns the position to an ocean using rough coordinate boxes:
const { latitude: lat, longitude: lon } = $('Wo ist die ISS?').first().json;
const now = new Date();
const solar = ((now.getUTCHours() + now.getUTCMinutes() / 60 + lon / 15) % 24 + 24) % 24;
const clock = String(Math.floor(solar)).padStart(2, '0') + ':' + String(Math.floor(solar % 1 * 60)).padStart(2, '0');
function ocean(lat, lon) {
if (lat < -55) return 'southern';
if (lat > 66) return 'arctic';
if (lon > 20 && lon < 147 && lat < 30) return 'indian';
if (lon > -100 && lon <= 20 && (lat > 8 || lon > -70)) return 'atlantic';
return 'pacific';
}
const geo = $('Was liegt darunter?').first().json;
const place = geo.error ? OCEANS[lang][ocean(lat, lon)] : geo.display_name;
The local time is solar time: UTC plus longitude divided by 15. It is not what the phones down there show, but it honestly tells you whether it is morning, noon or night below.
The AI stays honest and kind. The prompt contains only facts and rules: two to four sentences, playful, numbers exactly as given, nothing invented about the place beyond common knowledge. No jokes about war, disasters or tragedies; if the place is linked to one, the text stays neutral. The check node after it does not trust the answer blindly.
Limits against abuse. Every click costs an AI call. So: at most one card every 20 seconds for everybody together, five per hour per visitor, plus a daily cap. PHP checks this before n8n hears anything.
The site downloads the image itself. I could embed the image straight from NASA. But then every browser viewing a card would send a request, with its IP address, to a third-party server. I would also have to open the Content Security Policy to outside image sources. This way img-src 'self' stays, and the card still looks the same a year from now.
What the pattern is good for
The postcard is a toy. The pattern behind it is meant seriously: on request, turn live data into personal content, check it and store it under a fixed link. With that you can build, for example:
- a report a customer requests themselves, with their current numbers;
- attendance certificates after a training, with name and date;
- onboarding cards for new staff, with team, contacts and first appointment;
You will find more examples like this in my weekly n8n series, and if you would rather watch a workflow run in slow motion, press the button in the n8n live demo.
Try it yourself
You can download the workflow as JSON here. Secrets are replaced by placeholders.
- In n8n, choose βImport from Fileβ from the menu at the top right and load the file.
- In the βKI schreibt (IONOS)β node, create a Header Auth credential: name
Authorization, valueBearerplus your API key from an OpenAI-compatible provider. Adjust base URL and model if needed. - In the βAn die Seite sendenβ node, replace the URL and token placeholders with your own target, such as your own webhook, an email or a file.
- Activate the workflow and call the webhook URL once with
{"id": "test000001", "lang": "en"}.
Then go back to the orbit postcard: by now the ISS is a few thousand kilometres further on.
Originally published at daniele-michelin.com. More guides, free checks (SPF, TLS, headers) and an error-message lexicon: daniele-michelin.com/en/.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.