Skip to main content
Every GNet Platform integration comes down to two directions — Farm In (you receive trips) and Farm Out (you send trips). Each prompt below is a complete, self-contained build spec: paste it into your AI coding assistant and it generates a working integration — authentication, validation, idempotency, retries, status callbacks, and tests included. Then verify your Farm In endpoint with the black-box test at the bottom.
Establish your partnership in GNet Connect and get your API credentials before go-live. The prompts leave every credential as a placeholder — fill in the values we issue you.

How it works

1

Pick your direction

Farm In if other operators send trips to you. Farm Out if you send trips to partners. Most full integrations need both.
2

Fill the Variables block

Set your language, your griddID, and route paths at the top of the prompt. Leave credentials as placeholders — your assistant will wire them to environment variables.
3

Paste into your AI assistant

Drop the whole prompt into Cursor, Claude, ChatGPT, or Copilot. It returns a runnable project with tests and a README.
4

Verify, then go live

Run the Farm In conformance test below against your endpoint, and use the GBOOK tool to exercise trips end-to-end, before switching on production credentials.

Choose your language

The prompts are language-agnostic. Set one variable — TARGET_LANGUAGE_AND_FRAMEWORK — to your stack and the assistant generates idiomatic code for it. Verified targets:
StackExample frameworks
TypeScript / NodeExpress, NestJS, NextJS
PHPLaravel, plain PHP
JavaSpring Boot
PythonFastAPI, Flask
Any other stack works too — just name it in the slot.

The prompts

Builds your inbound POST endpoint (Basic auth + reservation JSON to success/error shape), the GET health check, QUOTE handling, and the outbound status callbacks to providerUpdateStatusByResNo. Expand, copy, fill the Variables block, and paste.
# GNet Platform — Farm In Integration (One-Shot Build Prompt)

> **How to use:** Fill in the `<<...>>` placeholders in the **Variables** block below, then paste this entire file into your AI coding assistant (Cursor, Claude, ChatGPT, Copilot, etc.). It will generate a complete, runnable Farm In integration in the language you specify. This block is the only thing you edit.

---

## Variables (fill these in)

```
TARGET_LANGUAGE_AND_FRAMEWORK = <<e.g. "TypeScript + Express + Zod" | "PHP + Laravel" | "Java + Spring Boot" | "Python + FastAPI">>
FARM_IN_ROUTE_PATH            = <<e.g. "/api/gnet-farmin">>
YOUR_GRIDDID                  = <<your GNet provider griddID, e.g. "acmelimo">>
INBOUND_BASIC_AUTH_USER       = <<api_key GNet issued you — you VERIFY this on inbound requests>>
INBOUND_BASIC_AUTH_SECRET     = <<api_secret GNet issued you — you VERIFY this on inbound requests>>
GATEWAY_UID                   = <<your API Gateway user id, for outbound getToken2>>
GATEWAY_PW                    = <<your API Gateway password, for outbound getToken2>>
```

> Treat every value above as configuration. Read them from environment variables / a secrets manager. Never hardcode secrets in source.

---

## Role & Context

You are a senior backend engineer. Build a **GNet Platform "Farm In" integration** in **`TARGET_LANGUAGE_AND_FRAMEWORK`**.

GNet is a B2B platform that connects ground-transportation operators and dispatch/reservation systems. In a Farm In flow, **another operator (the requester) sends a reservation to us (the provider)**. Our system must expose an inbound REST endpoint that **receives** these reservations from GNet, respond synchronously in GNet's expected shape, and then **push status updates back** to GNet as the trip progresses.

This integration has two halves:
1. **Inbound** — an HTTP endpoint GNet calls to deliver reservations (and a GET health check).
2. **Outbound** — calls *we* make to GNet to report status changes (and optionally GPS).

Produce production-quality, fully runnable code with tests. Where the spec is silent, make the safe, conventional choice and **list your assumptions explicitly** at the end. Do not invent credentials or URLs beyond those given here.

---

## Part 1 — Inbound endpoint (receive reservations)

Expose **`POST FARM_IN_ROUTE_PATH`**.

**Request GNet will send you**
- Headers: `Content-Type: application/json`, `Authorization: Basic <base64(INBOUND_BASIC_AUTH_USER:INBOUND_BASIC_AUTH_SECRET)>`.
- Body: a GNet reservation object. Representative payload (use this to model your types/DTOs):

```json
{
  "reservationDate": "-07:00",
  "affiliateReservation": {
    "action": "NEW",
    "status": "BOOKING_REQUEST",
    "requesterId": "gnettest",
    "requesterResNo": "1715016686",
    "providerId": "LATtest"
  },
  "reservationType": "REGULAR",
  "status": "BOOKING_REQUEST",
  "runType": "Airport",
  "passengerCount": "2",
  "eventName": "LA Convention Center",
  "passengers": [
    {
      "firstName": "Alex",
      "lastName": "Smith",
      "email": "alex@example.com",
      "phoneNumber": "+155550044444",
      "phones": [{ "number": "+155550044444", "type": "mobile" }]
    }
  ],
  "namesignInstructions": "Mr. Adam",
  "locations": {
    "pickup": {
      "locationType": "address",
      "address": "123 S Beverly Dr, Beverly Hills, CA 90212, USA",
      "city": "Beverly Hills", "state": "CA", "zipCode": "90212",
      "lat": 34.0664703, "lon": -118.3993243, "country": "US",
      "time": "2024-08-03T10:27:22"
    },
    "dropOff": {
      "locationType": "airport", "address": "LAX",
      "flightInfo": { "flightNumber": "123", "airlineCode": "UA" },
      "meetAndGreet": "False"
    },
    "stops": [
      {
        "locationType": "address",
        "address": "333 Santa Monica Blvd, Santa Monica, CA 90401, USA",
        "lat": 34.0164474, "lon": -118.4954529,
        "stopNumber": 1, "specialInstructions": "See Mr. Jones"
      }
    ]
  },
  "preferredVehicleType": "SEDAN",
  "specialInstructions": "- Carseat needed ",
  "sourceVendor": "GRIDD",
  "origination": "gBook",
  "notes": [
    { "message": "VIP Client", "context": "AFFILIATE" },
    { "message": "Drop off at Terminal 4 ", "context": "DRIVER" }
  ],
  "submitMode": "WAIT",
  "transactionId": "935a1056-644c-4e82-9a1a-6099efdd056b"
}
```

**Required handling**
1. **Authenticate** every request with HTTP Basic against `INBOUND_BASIC_AUTH_USER` / `INBOUND_BASIC_AUTH_SECRET`. Use a constant-time comparison. Reject bad/missing credentials with `401`.
2. **Validate** `Content-Type: application/json` and parse defensively. On malformed JSON or missing required fields, return the documented error shape (below).
3. **Idempotency:** `transactionId` is the unique key for the transaction. Persist a record of processed `transactionId`s. If you receive a `transactionId` you have already processed, return the **same** prior result instead of creating a duplicate.
4. **NEW vs UPDATE:** branch on `affiliateReservation.action`. `"NEW"` creates a reservation; `"UPDATE"` modifies the existing one (match on `transactionId` / `requesterResNo`). Handle an unknown `action` gracefully with a clear error.
5. **QUOTE handling:** if `reservationType == "QUOTE"`, **do not** persist a reservation. Compute and return a price in `totalAmount` using the same success shape below. (Pricing logic is a stub/hook for the operator to implement.)
6. Map GNet `preferredVehicleType` to your internal fleet types, and GNet `status` codes to your internal statuses, in a single dedicated mapping module (see Reference links).
7. Preserve timestamps as received. They are ISO-8601 and may carry a UTC offset (`reservationDate` may be just an offset like `"-07:00"`; `locations.*.time` looks like `"2024-08-03T10:27:22"`). Do not silently coerce to server-local time.

**Success response** — HTTP `200`:
```json
{ "success": true, "reservationId": "11545-001", "totalAmount": "130.67", "transactionId": "<echo back the request transactionId>" }
```

**Error response:**
```json
{ "success": false, "message": "Unable to process the request due to <reason>.", "transactionId": "<echo back if present>" }
```
> GNet keys off the `success` boolean. Return `401` for auth failures and `400` for unparseable requests; for business failures return the `success:false` body. (If your GNet onboarding contact specifies exact HTTP codes for business errors, follow those — flag this as an assumption.)

## Part 2 — Health check

`GET FARM_IN_ROUTE_PATH` must return HTTP `200` with:
```json
{ "success": true }
```
GNet uses this GET as a liveness probe. It must not require auth-protected resources to answer.

## Part 3 — Outbound status updates (report progress back to GNet)

As the trip progresses, push status changes to GNet. First obtain a token, then call the update endpoint.

**Get a token** (cache it; refresh on expiry or on a `401`):
```
POST https://api.grdd.net/Platform.svc/getToken2
Content-Type: application/json

{ "uid": "GATEWAY_UID", "pw": "GATEWAY_PW" }
```
Response contains a `token` string used as a header on subsequent calls.

**Update status by reservation number:**
```
POST https://api.grdd.net/Platform.svc/providerUpdateStatusByResNo/{YOUR_GRIDDID}/{RESNO}/{VERSION}
token: <token from getToken2>
Content-Type: application/json

{ "status": "CONFIRMED", "totalAmount": "120.00", "resNo": "<reservation_number>", "griddID": "YOUR_GRIDDID" }
```
- Version segment: the reference specifies `V1` (the Quick Start shows it lower-case as `v1`); use what your onboarding contact confirms.
- Common statuses: `CONFIRMED`, `ASSIGNED`, `EN_ROUTE`, `ON_LOCATION` (full list at the Status Codes reference). A `CONFIRMED` reply with `totalAmount` is how you accept a booking request.
- A by-Transaction-ID variant also exists (`providerUpdateStatusByTransactionId`) — expose both behind one `reportStatus(...)` function.

## Part 4 — Optional: GPS location updates

If the operator wants live tracking, implement:
```
POST https://location.grdd.net/api/GGPS.svc/saveGPScache
token: <token from getToken2>
Content-Type: application/json

{ "chfName": "Jane Doe", "driverId": "123456", "griddid": "YOUR_GRIDDID",
  "internalBookingId": "123456", "latitude": "40.74", "longitude": "-100.56",
  "locationDateTime": "2024-01-23T18:52:34", "phoneNo": "+15555555555", "transactionId": "<>" }
```
Make this an opt-in module, off by default.

---

## Non-functional requirements (apply to all of the above)

- **Config & secrets:** all URLs, IDs, and credentials come from environment/config. Nothing secret in source or logs.
- **Outbound resilience:** every outbound call (token, status, GPS) has a timeout and retries with exponential backoff + jitter, capped attempts, and treats only `2xx` as success. A `401` triggers one token refresh + retry.
- **Idempotency store:** pluggable interface (in-memory default + a note on swapping for Redis/DB). The platform stack here is Next.js + Azure + Redis, so make Redis the obvious production choice.
- **Logging:** structured logs correlated by `transactionId`. Never log `Authorization`, tokens, or passenger PII (names, phones, emails) above debug level.
- **Validation & mapping:** isolate request validation, status mapping, and vehicle-type mapping into their own modules so they can evolve with the spec.
- **Concurrency-safe** under parallel requests for the same `transactionId`.
- **Tests** (use the idiomatic framework for the language): happy-path `NEW`, `UPDATE`, `QUOTE` (no persistence, returns `totalAmount`), bad/missing Basic auth → `401`, malformed JSON → `400`, duplicate `transactionId` is idempotent, `GET` health check returns `200 {success:true}`, and an outbound `reportStatus` test with the token-refresh-on-401 path mocked.

## Deliverables

1. A complete, runnable project in `TARGET_LANGUAGE_AND_FRAMEWORK` with a clear file layout.
2. The inbound POST + GET handlers, the outbound GNet client (token caching, status, GPS), validation, and mapping modules.
3. `.env.example` listing every variable from the Variables block.
4. Tests covering the cases above.
5. A `README` with run instructions and this verification snippet:
   ```bash
   # health check
   curl -X GET "http://localhost:PORT/FARM_IN_ROUTE_PATH" -i
   # simulate an inbound reservation (Basic auth = INBOUND_BASIC_AUTH_USER:INBOUND_BASIC_AUTH_SECRET)
   curl -X POST "http://localhost:PORT/FARM_IN_ROUTE_PATH" \
     -u "USER:SECRET" -H "Content-Type: application/json" \
     -d @sample-reservation.json
   ```
6. A short list of **assumptions** you made.

## Acceptance criteria (self-check before finishing)

- [ ] `GET` health check returns `200 {"success": true}` with no auth.
- [ ] `POST` rejects missing/incorrect Basic auth with `401` (constant-time compare).
- [ ] Valid `NEW` reservation returns `{success:true, reservationId, totalAmount, transactionId}`.
- [ ] Re-sending the same `transactionId` does not create a duplicate.
- [ ] `reservationType:"QUOTE"` returns `totalAmount` and persists nothing.
- [ ] Outbound `reportStatus` obtains/caches a token and posts to `providerUpdateStatusByResNo`, refreshing the token on `401`.
- [ ] No secrets or PII in logs; all config externalized.

## Reference (consult for field-level detail; do not hardcode anything beyond the Variables block)

- Quick Start: https://docs.grdd.dev/gnet-platform/quick-start
- Receiving Reservations: https://docs.grdd.dev/platform-api/receiving-reservations
- Reservation Object Model: https://docs.grdd.dev/platform-api/reference/object-model
- Status Codes: https://docs.grdd.dev/platform-api/reference/status-codes
- Vehicle Types: https://docs.grdd.dev/platform-api/reference/vehicle-types
- Update Status by ResNo: https://docs.grdd.dev/platform-api/provider-updates/update-by-resno
- Update Status by Transaction ID: https://docs.grdd.dev/platform-api/provider-updates/update-by-transaction-id
- Get Token: https://docs.grdd.dev/platform-api/authentication/get-token
- Save GPS: https://docs.grdd.dev/gnet-api/location/save-GPS
- Full sample reservation: https://raw.githubusercontent.com/GRiDD/GRiDD.github.io/main/gnet-sample-reservation.json
- API reference (Apiary): https://gnet.docs.apiary.io/
- Test inbound trips with GBOOK: https://api.grdd.net/gbook  (BOOK → pick your griddID → "SHOW JSON" reveals the exact payload)

Now build it.

Test your Farm In endpoint

Once your Farm In service is built and deployed, verify it against the contract with this black-box script. It plays the role of GNet — sending representative reservations to your URL and checking the responses — so you need no access to your own code to run it. Pure bash + curl, with 0/1 exit codes for CI.
FARMIN_URL=https://api.example.com/api/gnet-farmin \
API_KEY=your_api_key API_SECRET=your_api_secret \
PROVIDER_GRIDDID=yourgriddid \
./gnet-farmin-blackbox-test.sh
It checks:
  • GET health → 200 {"success": true}
  • Basic auth enforced — missing and wrong credentials are rejected with 401
  • REGULAR NEWsuccess + reservationId + the transactionId echoed back
  • Idempotency — the same transactionId returns the same reservationId (no duplicate booking)
  • QUOTE → returns a totalAmount
  • Malformed JSON → rejected with 400, not a 5xx crash
#!/usr/bin/env bash
#
# GNet Platform — Farm In black-box conformance test
# -----------------------------------------------------
# Points at a developer's Farm In endpoint and verifies it conforms to the
# GNet contract: it plays the role of GNet sending reservations, then checks
# the responses. No code access required — pure HTTP black-box testing.
#
# Contract reference: https://docs.grdd.dev/gnet-platform/quick-start
#
# USAGE
#   FARMIN_URL=https://api.example.com/api/gnet-farmin \
#   API_KEY=your_api_key API_SECRET=your_api_secret \
#   PROVIDER_GRIDDID=acmelimo \
#   ./gnet-farmin-blackbox-test.sh
#
#   # or pass the URL as the first argument:
#   ./gnet-farmin-blackbox-test.sh https://api.example.com/api/gnet-farmin
#
# ENV VARS
#   FARMIN_URL         (required) the Farm In endpoint under test
#   API_KEY/API_SECRET (required) Basic-auth creds the endpoint expects (the ones GNet issued)
#   PROVIDER_GRIDDID   (required) griddID of the operator under test -> used as providerId
#   REQUESTER_GRIDDID  (optional) simulated sender, default "gnettest"
#   VERBOSE=1          (optional) print full response bodies
#
# EXIT CODE: 0 if all checks PASS (WARN allowed), 1 if any FAIL. CI-friendly.
#
# Dependencies: bash + curl. Uses jq if present, otherwise a grep fallback.

set -uo pipefail

FARMIN_URL="${1:-${FARMIN_URL:-}}"
API_KEY="${API_KEY:-}"
API_SECRET="${API_SECRET:-}"
PROVIDER_GRIDDID="${PROVIDER_GRIDDID:-}"
REQUESTER_GRIDDID="${REQUESTER_GRIDDID:-gnettest}"
VERBOSE="${VERBOSE:-0}"
TIMEOUT=20

# ---- pretty output (color only on a TTY) ------------------------------------
if [ -t 1 ]; then C_G=$'\033[32m'; C_R=$'\033[31m'; C_Y=$'\033[33m'; C_B=$'\033[1m'; C_0=$'\033[0m'
else C_G=""; C_R=""; C_Y=""; C_B=""; C_0=""; fi

PASS=0; FAIL=0; WARN=0
pass() { PASS=$((PASS+1)); printf "  %s[PASS]%s %s\n" "$C_G" "$C_0" "$1"; }
fail() { FAIL=$((FAIL+1)); printf "  %s[FAIL]%s %s\n" "$C_R" "$C_0" "$1"; [ -n "${2:-}" ] && printf "         %s\n" "$2"; }
warn() { WARN=$((WARN+1)); printf "  %s[WARN]%s %s\n" "$C_Y" "$C_0" "$1"; [ -n "${2:-}" ] && printf "         %s\n" "$2"; }
hdr()  { printf "\n%s%s%s\n" "$C_B" "$1" "$C_0"; }

# ---- prerequisites ----------------------------------------------------------
[ -z "$FARMIN_URL" ] && { echo "ERROR: FARMIN_URL is required (env var or first arg)."; exit 2; }
command -v curl >/dev/null 2>&1 || { echo "ERROR: curl is required."; exit 2; }
HAVE_JQ=0; command -v jq >/dev/null 2>&1 && HAVE_JQ=1
[ -z "$PROVIDER_GRIDDID" ] && warn "PROVIDER_GRIDDID not set — using placeholder 'PROVIDER' in payloads."
PROVIDER_GRIDDID="${PROVIDER_GRIDDID:-PROVIDER}"
if [ -z "$API_KEY" ] || [ -z "$API_SECRET" ]; then
  warn "API_KEY/API_SECRET not set — authenticated checks will likely fail until you supply them."
fi

# ---- helpers ----------------------------------------------------------------
gen_uuid() {
  if command -v uuidgen >/dev/null 2>&1; then uuidgen | tr 'A-Z' 'a-z'
  elif [ -r /proc/sys/kernel/random/uuid ]; then cat /proc/sys/kernel/random/uuid
  else python3 - <<'PY' 2>/dev/null || echo "00000000-0000-4000-8000-$(date +%s%N | cut -c1-12)"
import uuid; print(uuid.uuid4())
PY
  fi
}

# json_field <body> <key> -> value (string unquoted, or bare true/false/number)
json_field() {
  if [ "$HAVE_JQ" = "1" ]; then
    printf '%s' "$1" | jq -r --arg k "$2" '.[$k] // empty' 2>/dev/null
  else
    printf '%s' "$1" | grep -oE "\"$2\"[[:space:]]*:[[:space:]]*(\"[^\"]*\"|true|false|[0-9.]+)" \
      | head -n1 | sed -E "s/^[^:]*:[[:space:]]*//; s/^\"//; s/\"$//"
  fi
}

# CODE and BODY are set by request()
CODE=""; BODY=""
request() { # request <METHOD> <auth: none|good|bad> [payload]
  local method="$1" auth="$2" payload="${3:-}"
  local tmp; tmp="$(mktemp)"
  local -a args=(-sS -m "$TIMEOUT" -o "$tmp" -w "%{http_code}" -X "$method" "$FARMIN_URL" -H "Content-Type: application/json")
  case "$auth" in
    good) args+=(-u "${API_KEY}:${API_SECRET}") ;;
    bad)  args+=(-u "${API_KEY}:wrong-secret-$(date +%s)") ;;
    none) : ;;
  esac
  [ -n "$payload" ] && args+=(--data "$payload")
  CODE="$(curl "${args[@]}" 2>/dev/null)"; local rc=$?
  BODY="$(cat "$tmp")"; rm -f "$tmp"
  if [ $rc -ne 0 ]; then CODE="000"; fi
  [ "$VERBOSE" = "1" ] && printf "      -> HTTP %s | %s\n" "$CODE" "$(printf '%s' "$BODY" | tr '\n' ' ' | cut -c1-300)"
}

# reservation payload builder
reservation() { # reservation <txid> <reservationType> <action> <resno>
  cat <<JSON
{
  "reservationDate": "-07:00",
  "affiliateReservation": { "action": "$3", "status": "BOOKING_REQUEST", "requesterId": "${REQUESTER_GRIDDID}", "requesterResNo": "$4", "providerId": "${PROVIDER_GRIDDID}" },
  "reservationType": "$2",
  "status": "BOOKING_REQUEST",
  "runType": "Airport",
  "passengerCount": "2",
  "passengers": [ { "firstName": "Test", "lastName": "Passenger", "email": "test@example.com", "phones": [ { "number": "+15555550144", "type": "mobile" } ] } ],
  "locations": {
    "pickup": { "locationType": "address", "address": "123 S Beverly Dr, Beverly Hills, CA 90212, USA", "lat": 34.0664703, "lon": -118.3993243, "time": "2030-01-15T10:27:22" },
    "dropOff": { "locationType": "airport", "address": "LAX", "flightInfo": { "flightNumber": "123", "airlineCode": "UA" }, "meetAndGreet": "False" },
    "stops": []
  },
  "preferredVehicleType": "SEDAN",
  "specialInstructions": "GNet Farm In conformance test — safe to ignore",
  "submitMode": "WAIT",
  "transactionId": "$1"
}
JSON
}

is_success_true() { [ "$(json_field "$1" success)" = "true" ]; }

printf "%sGNet Farm In — black-box conformance test%s\n" "$C_B" "$C_0"
printf "Target: %s\n" "$FARMIN_URL"
printf "Requester: %s  ->  Provider: %s  | jq: %s\n" "$REQUESTER_GRIDDID" "$PROVIDER_GRIDDID" "$([ "$HAVE_JQ" = 1 ] && echo yes || echo 'no (grep fallback)')"

# ---- 1. Health check (GET) --------------------------------------------------
hdr "1. Health check — GET should return 200 {\"success\": true}"
request GET none
if [ "$CODE" = "200" ] && is_success_true "$BODY"; then pass "GET returns 200 with success:true"
elif [ "$CODE" = "200" ]; then warn "GET returned 200 but body is not {\"success\": true}" "body: $(printf '%s' "$BODY" | cut -c1-160)"
elif [ "$CODE" = "000" ]; then fail "Could not reach the endpoint (connection error / timeout)."
else fail "GET expected 200, got $CODE" "GNet uses this GET as a liveness probe; it should be public."; fi

# ---- 2. Auth: missing credentials ------------------------------------------
hdr "2. Security — POST without credentials should be rejected"
request POST none "$(reservation "$(gen_uuid)" REGULAR NEW "rt-noauth-$(date +%s)")"
if [ "$CODE" = "401" ] || [ "$CODE" = "403" ]; then pass "Unauthenticated POST rejected with $CODE"
elif is_success_true "$BODY"; then fail "Unauthenticated POST was ACCEPTED (success:true) — endpoint is not enforcing Basic auth." "Expected 401."
else warn "Unauthenticated POST not a clean 401 (got $CODE) but did not succeed."; fi

# ---- 3. Auth: wrong credentials --------------------------------------------
hdr "3. Security — POST with wrong credentials should be rejected"
request POST bad "$(reservation "$(gen_uuid)" REGULAR NEW "rt-badauth-$(date +%s)")"
if [ "$CODE" = "401" ] || [ "$CODE" = "403" ]; then pass "Wrong-credential POST rejected with $CODE"
elif is_success_true "$BODY"; then fail "Wrong-credential POST was ACCEPTED — Basic auth is not validated." "Expected 401."
else warn "Wrong-credential POST not a clean 401 (got $CODE) but did not succeed."; fi

# ---- 4. REGULAR NEW reservation --------------------------------------------
hdr "4. REGULAR NEW — valid reservation should return success + reservationId"
TXID_NEW="$(gen_uuid)"; RESNO_NEW="rt-new-$(date +%s)"
request POST good "$(reservation "$TXID_NEW" REGULAR NEW "$RESNO_NEW")"
RID_FIRST="$(json_field "$BODY" reservationId)"
if [ "$CODE" = "200" ] && is_success_true "$BODY"; then
  pass "NEW returns 200 with success:true"
  [ -n "$RID_FIRST" ] && pass "Response includes reservationId ($RID_FIRST)" || fail "Response missing reservationId" "Per the contract the success body must include reservationId."
  ECHO_TX="$(json_field "$BODY" transactionId)"
  if [ -n "$ECHO_TX" ]; then
    [ "$ECHO_TX" = "$TXID_NEW" ] && pass "transactionId echoed back correctly" || warn "transactionId echoed but does not match what was sent" "sent $TXID_NEW, got $ECHO_TX"
  else warn "Response did not echo transactionId" "Recommended so the sender can correlate."; fi
else
  fail "NEW expected 200 success:true, got HTTP $CODE" "body: $(printf '%s' "$BODY" | cut -c1-200)"
fi

# ---- 5. Idempotency — resend same transactionId ----------------------------
hdr "5. Idempotency — resending the same transactionId must not duplicate"
request POST good "$(reservation "$TXID_NEW" REGULAR NEW "$RESNO_NEW")"
RID_SECOND="$(json_field "$BODY" reservationId)"
if [ "$CODE" = "200" ] && is_success_true "$BODY"; then
  if [ -n "$RID_FIRST" ] && [ "$RID_SECOND" = "$RID_FIRST" ]; then pass "Same transactionId returns the same reservationId ($RID_SECOND) — idempotent"
  elif [ -z "$RID_FIRST" ]; then warn "Could not compare reservationIds (first response had none)."
  else fail "Resend produced a DIFFERENT reservationId ($RID_FIRST -> $RID_SECOND) — possible duplicate booking." "Dedupe on transactionId."; fi
else fail "Idempotent resend expected 200 success:true, got HTTP $CODE"; fi

# ---- 6. QUOTE ---------------------------------------------------------------
hdr "6. QUOTE — reservationType QUOTE should return a totalAmount"
request POST good "$(reservation "$(gen_uuid)" QUOTE NEW "rt-quote-$(date +%s)")"
if [ "$CODE" = "200" ] && is_success_true "$BODY"; then
  AMT="$(json_field "$BODY" totalAmount)"
  [ -n "$AMT" ] && pass "QUOTE returns success with totalAmount ($AMT)" || fail "QUOTE returned success but no totalAmount" "QUOTE responses must include totalAmount."
else fail "QUOTE expected 200 success:true, got HTTP $CODE" "body: $(printf '%s' "$BODY" | cut -c1-200)"; fi

# ---- 7. Malformed JSON ------------------------------------------------------
hdr "7. Robustness — malformed JSON should be rejected (not 5xx-crash)"
request POST good '{ "this is": not valid json, }'
if [ "$CODE" = "400" ] || [ "$CODE" = "422" ]; then pass "Malformed JSON rejected with $CODE"
elif [ "${CODE:0:1}" = "4" ]; then pass "Malformed JSON rejected with $CODE (4xx)"
elif is_success_true "$BODY"; then fail "Malformed JSON was accepted as success — input is not validated."
elif [ "${CODE:0:1}" = "5" ]; then warn "Malformed JSON caused a $CODE server error — prefer a clean 400."
else warn "Malformed JSON returned $CODE (expected 400)."; fi

# ---- 8. UPDATE (informational) ---------------------------------------------
hdr "8. UPDATE — action UPDATE on the known trip (informational)"
request POST good "$(reservation "$TXID_NEW" REGULAR UPDATE "$RESNO_NEW")"
if [ "$CODE" = "200" ] && is_success_true "$BODY"; then pass "UPDATE accepted with success:true"
else warn "UPDATE returned HTTP $CODE (UPDATE handling varies by operator)" "body: $(printf '%s' "$BODY" | cut -c1-160)"; fi

# ---- summary ----------------------------------------------------------------
printf "\n%s──────── Summary ────────%s\n" "$C_B" "$C_0"
printf "  %sPASS %d%s   %sWARN %d%s   %sFAIL %d%s\n" "$C_G" "$PASS" "$C_0" "$C_Y" "$WARN" "$C_0" "$C_R" "$FAIL" "$C_0"
if [ "$FAIL" -eq 0 ]; then printf "  %sFarm In endpoint looks conformant.%s\n" "$C_G" "$C_0"; exit 0
else printf "  %s%d check(s) failed — see above.%s\n" "$C_R" "$FAIL" "$C_0"; exit 1; fi

Keep going

Quick Start

The full technical integration guide behind these prompts.

API Reference

Complete endpoint reference on Apiary.

Test with GBOOK

Fire test trips into your integration end-to-end. Use “SHOW JSON” to see exact payloads.

Need a hand?

Our team can walk through the integration with you.