API Platform
Production docs for integrating RelayForge into tourism websites: tours, availability, bookings, content, forms, media delivery, and multilingual translations.
WordPress plugin
Prefer a no-code WordPress install? Download the RelayForge WordPress plugin, add your API key, and publish tours, packages, reviews, availability, and forms with shortcodes.
Download WordPress pluginQuickstart
- Generate tenant API key in RelayForge admin.
- In
/dashboard/settings, set Public website URL (used by Translate website now crawler). - Fetch tours and render cards on the website.
- Fetch availability for tour/date selectors.
- Submit inquiry or booking-intent via forms endpoint.
- Load tenant locales and generate per-page translations before render.
const headers = { "X-API-Key": "rf_live_your_api_key" };
const localeConfig = await fetch("https://relay.forgelabspro.com/api/v1/translations/locales", { headers }).then(r => r.json());
const tours = await fetch("https://relay.forgelabspro.com/api/v1/tours?limit=6", { headers }).then(r => r.json());
const availability = await fetch("https://relay.forgelabspro.com/api/v1/availability?tourId=tour_1", { headers }).then(r => r.json());
const translatedHome = await fetch("https://relay.forgelabspro.com/api/v1/translations/generate", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ pageId: "home", pageSlug: "home", sourceText: "Welcome", sourceLocale: "en", sourceVersion: "v1" }),
}).then(r => r.json());
await fetch("https://relay.forgelabspro.com/api/v1/forms/submit", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ formSlug: "inquiry-form", response: { fullName: "Amina" } }),
});
Localization flow (full integration)
Use this exact sequence on tenant websites to show the right languages and render translated content.
Admin note: Translate website now crawls from the tenant Public website URL saved in/dashboard/settings. If that URL is not set or crawl fails, RelayForge falls back to internal published content (blogs, tours, destinations).
- Call
GET /api/v1/translations/locales once on app startup. - Build the language switcher from
enabledLocales. - If user-selected locale is not enabled, fallback to
defaultLocale. - For each page, call
POST /api/v1/translations/generate with page source text and version. - Render
data.translations[currentLocale]; fallback to source text when missing. - Bump
sourceVersion when source content changes to force fresh translations.
Copy-paste loader example
const API_BASE = "https://relay.forgelabspro.com";
const headers = { "X-API-Key": "rf_live_your_api_key" };
export async function getTenantLocales() {
const res = await fetch(API_BASE + "/api/v1/translations/locales", { headers });
const json = await res.json();
if (!res.ok || !json.ok) throw new Error(json.error || "Failed to load locales");
return json.data;
}
export async function getTranslatedPageContent({
pageId,
pageSlug,
sourceText,
sourceLocale = "en",
sourceVersion,
locale,
}) {
const localeConfig = await getTenantLocales();
const enabled = localeConfig.enabledLocales || [];
const currentLocale = enabled.includes(locale) ? locale : localeConfig.defaultLocale || sourceLocale;
const res = await fetch(API_BASE + "/api/v1/translations/generate", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
pageId,
pageSlug,
sourceText,
sourceLocale,
sourceVersion,
targetLocales: [currentLocale],
}),
});
const json = await res.json();
if (!res.ok || !json.ok) throw new Error(json.error || "Translation failed");
return {
locale: currentLocale,
content: json.data?.translations?.[currentLocale] || sourceText,
enabledLocales: enabled,
defaultLocale: localeConfig.defaultLocale,
};
}
Authentication
Supported auth inputs: `X-API-Key`, `Authorization: Bearer`, or `?apiKey=` query.
curl "https://relay.forgelabspro.com/api/v1/tours?limit=10" \
-H "X-API-Key: rf_live_your_api_key"
Error handling
Common API errors
| Field | Type | Notes |
|---|
| 400 | Bad Request | Invalid query/body (status, date, malformed JSON) |
| 401 | Unauthorized | Missing, invalid, or revoked API key |
| 404 | Not Found | Resource/file not found |
| 500 | Server Error | Unexpected backend failure |
if (!res.ok) {
const err = await res.json().catch(() => ({ error: "Request failed" }));
throw new Error(err.error || "Request failed");
}
GET
/api/v1/tours
Returns active published tours with package metadata, pricing, and language info.
Query params
| Field | Type | Notes |
|---|
| limit | number | 1-100, default 50 |
| offset | number | >= 0, default 0 |
Response schema
| Field | Type | Notes |
|---|
| ok | boolean | True on success |
| data.tours[] | array | List of published tours |
| data.tours[].id | string | Tour ID |
| data.tours[].title | string | Tour title |
| data.tours[].price | number | Base price |
| data.tours[].packageMeta | object|null | Enriched package details |
| data.total | number | Total matching records |
Endpoint errors
| Field | Type | Notes |
|---|
| 401 | Error | Invalid or revoked API key |
| 500 | Error | Failed to fetch tours |
curl example
curl "https://relay.forgelabspro.com/api/v1/tours?limit=6&offset=0" \
-H "X-API-Key: rf_live_your_api_key"
JavaScript example
const res = await fetch("https://relay.forgelabspro.com/api/v1/tours?limit=6&offset=0", {
headers: { "X-API-Key": "rf_live_your_api_key" },
});
const payload = await res.json();
const tours = payload.data?.tours || [];
Sample response
{
"ok": true,
"data": {
"tours": [{ "id": "tour_1", "title": "Masai Mara 3 Days", "price": 1200 }],
"total": 42,
"limit": 6,
"offset": 0
}
}
GET
/api/v1/availability
Returns per-date inventory with remaining seats for selected tours/date windows.
Query params
| Field | Type | Notes |
|---|
| tourId | string | Optional tour filter |
| from | string | Optional YYYY-MM-DD |
| to | string | Optional YYYY-MM-DD |
| limit | number | 1-100 |
| offset | number | >= 0 |
Response schema
| Field | Type | Notes |
|---|
| data.availability[] | array | Availability rows |
| data.availability[].date | string | ISO date |
| data.availability[].totalSlots | number | Total seats |
| data.availability[].bookedSlots | number | Booked seats |
| data.availability[].remainingSlots | number | Computed remaining seats |
Endpoint errors
| Field | Type | Notes |
|---|
| 400 | Error | Invalid date format (use YYYY-MM-DD) |
| 401 | Error | Invalid or revoked API key |
| 500 | Error | Failed to fetch availability |
curl example
curl "https://relay.forgelabspro.com/api/v1/availability?tourId=tour_1&from=2026-09-01&to=2026-09-30" \
-H "Authorization: Bearer rf_live_your_api_key"
JavaScript example
const url = "https://relay.forgelabspro.com/api/v1/availability?tourId=tour_1&from=2026-09-01&to=2026-09-30";
const res = await fetch(url, { headers: { "X-API-Key": "rf_live_your_api_key" } });
const data = await res.json();
Sample response
{
"ok": true,
"data": {
"availability": [{ "date": "2026-09-12", "remainingSlots": 8 }]
}
}
GET
/api/v1/bookings
Returns customer bookings for CRM syncs and operations dashboards.
Query params
| Field | Type | Notes |
|---|
| status | string | pending | confirmed | cancelled (optional) |
| limit | number | 1-100 |
| offset | number | >= 0 |
Response schema
| Field | Type | Notes |
|---|
| data.bookings[] | array | Booking list |
| data.bookings[].id | string | Booking ID |
| data.bookings[].customerName | string | Lead traveler name |
| data.bookings[].numberOfPeople | number | Party size |
| data.bookings[].status | string | Booking status |
Endpoint errors
| Field | Type | Notes |
|---|
| 400 | Error | Invalid status filter |
| 401 | Error | Invalid or revoked API key |
| 500 | Error | Failed to fetch bookings |
curl example
curl "https://relay.forgelabspro.com/api/v1/bookings?status=confirmed&limit=20" \
-H "X-API-Key: rf_live_your_api_key"
JavaScript example
const res = await fetch("https://relay.forgelabspro.com/api/v1/bookings?status=confirmed&limit=20", {
headers: { "X-API-Key": "rf_live_your_api_key" },
});
const bookings = (await res.json()).data?.bookings || [];
Sample response
{
"ok": true,
"data": {
"bookings": [{ "id": "bk_1", "customerName": "Amina", "status": "confirmed" }]
}
}
GET
/api/v1/destinations | /blogs | /reviews
Website content feeds for destination pages, SEO blogs, and approved reviews.
Query params
| Field | Type | Notes |
|---|
| limit | number | For list endpoints |
| offset | number | For list endpoints |
| tourId | string | Optional on /reviews |
Response schema
| Field | Type | Notes |
|---|
| data.destinations[] | array | Destination records |
| data.blogs[] | array | Published blog records |
| data.reviews[] | array | Approved review records |
Endpoint errors
| Field | Type | Notes |
|---|
| 401 | Error | Invalid or revoked API key |
| 500 | Error | Failed to fetch content |
curl example
curl "https://relay.forgelabspro.com/api/v1/destinations?limit=12" -H "X-API-Key: rf_live_your_api_key"
curl "https://relay.forgelabspro.com/api/v1/blogs?limit=5" -H "X-API-Key: rf_live_your_api_key"
curl "https://relay.forgelabspro.com/api/v1/reviews?tourId=tour_1&limit=10" -H "X-API-Key: rf_live_your_api_key"
JavaScript example
const headers = { "X-API-Key": "rf_live_your_api_key" };
const [destinations, blogs, reviews] = await Promise.all([
fetch("https://relay.forgelabspro.com/api/v1/destinations?limit=12", { headers }).then(r => r.json()),
fetch("https://relay.forgelabspro.com/api/v1/blogs?limit=5", { headers }).then(r => r.json()),
fetch("https://relay.forgelabspro.com/api/v1/reviews?limit=10", { headers }).then(r => r.json()),
]);
Sample response
{
"ok": true,
"data": {
"destinations": [{ "slug": "masai-mara" }],
"blogs": [{ "slug": "best-time" }],
"reviews": [{ "rating": 5 }]
}
}
GET
/api/v1/translations/locales
Returns tenant language configuration for locale switchers and fallback behavior.
Response schema
| Field | Type | Notes |
|---|
| ok | boolean | True on success |
| data.defaultLocale | string | Default/fallback locale |
| data.enabledLocales[] | array | Enabled locales selected in tenant settings |
| data.languageCount | number | Count of enabled locales |
| data.autoTranslateOnPageView | boolean | Whether first page request should trigger translation |
Endpoint errors
| Field | Type | Notes |
|---|
| 401 | Error | Invalid or revoked API key |
| 500 | Error | Failed to load tenant locales |
curl example
curl "https://relay.forgelabspro.com/api/v1/translations/locales" \
-H "X-API-Key: rf_live_your_api_key"
JavaScript example
const res = await fetch("https://relay.forgelabspro.com/api/v1/translations/locales", {
headers: { "X-API-Key": "rf_live_your_api_key" },
});
const payload = await res.json();
const locales = payload.data?.enabledLocales || [];
const defaultLocale = payload.data?.defaultLocale || "en";
Sample response
{
"ok": true,
"data": {
"defaultLocale": "en",
"enabledLocales": ["en", "fr", "de", "es", "it", "sw"],
"languageCount": 6,
"autoTranslateOnPageView": true
}
}
POST
/api/v1/translations/generate
Translates and stores page content in RelayForge DB. Reuses existing translations for unchanged source hashes.
Request body
| Field | Type | Notes |
|---|
| pageId | string | Stable page key, e.g. home/about/tour-123 |
| pageSlug | string | Page slug |
| sourceText | string | Source content text to translate |
| sourceLocale | string | Source locale, usually en |
| sourceVersion | string | Version/hash marker for cache invalidation |
| targetLocales | string[] | Optional; omit to use tenant configured locales |
Response schema
| Field | Type | Notes |
|---|
| data.translations | object | Map keyed by locale code |
| data.created[] | array | Locales translated during this call |
| data.failed[] | array | Locales that failed with error details |
| data.sourceHash | string | Computed hash used for dedupe |
Endpoint errors
| Field | Type | Notes |
|---|
| 400 | Error | Invalid body or translation provider failure |
| 401 | Error | Invalid or revoked API key |
curl example
curl -X POST "https://relay.forgelabspro.com/api/v1/translations/generate" \
-H "Content-Type: application/json" \
-H "X-API-Key: rf_live_your_api_key" \
-d '{
"pageId": "home",
"pageSlug": "home",
"sourceText": "Welcome to our safari tours",
"sourceLocale": "en",
"sourceVersion": "v1"
}'
JavaScript example
const res = await fetch("https://relay.forgelabspro.com/api/v1/translations/generate", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "rf_live_your_api_key",
},
body: JSON.stringify({
pageId: "home",
pageSlug: "home",
sourceText: "Welcome to our safari tours",
sourceLocale: "en",
sourceVersion: "v1",
}),
});
const payload = await res.json();
const translated = payload.data?.translations?.fr || null;
Sample response
{
"ok": true,
"data": {
"pageId": "home",
"sourceLocale": "en",
"sourceVersion": "v1",
"sourceHash": "sha256_hash",
"translations": { "fr": "Bienvenue dans nos safaris" },
"created": ["fr"],
"failed": []
}
}
Security and production checklist
- Keep API keys on your backend proxy whenever possible.
- Rotate API keys regularly and after team member changes.
- Cache GET endpoints (`tours`, `destinations`, `blogs`) aggressively.
- Implement retries with backoff for intermittent `500` errors.
- Validate user-submitted fields before calling forms submit endpoint.