> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grdd.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration FAQ

> The questions integrators ask most often — multi-tenant setup, credentials, webhooks, payload rules, and the mistakes that cost the most time

This page answers the questions that come up repeatedly during GNet Platform integrations, in the order they usually come up. For the full field-by-field reference see the [Object Model](/platform-api/reference/object-model); for a guided build, start with the [Quick Start Guide](/gnet-platform/quick-start).

<Note>
  Farm In and Farm Out are **separate integrations**. They do not share events, they are [certified](/gnet-platform/certification) separately, and each can go live on its own. Many members ship one, run it with real partners for a while, and add the other later.
</Note>

## Integration model

<AccordionGroup>
  <Accordion title="What is the difference between GNet Connect and GNet Platform?">
    **GNet Connect** is the marketplace and trust layer: public profiles, verified documents, partnership requests. **GNet Platform** is the API that exchanges reservations in real time once those partnerships exist.

    Connect is architecturally prior to Platform. Two companies must be members and must have an established partnership before trips can flow between them.

    Connect subscription tiers do **not** gate Platform API access. Farm In, Farm Out, Pinger, Get Documents, Content API and Search Members are available regardless of Connect plan.
  </Accordion>

  <Accordion title="Are we a requester, a provider, or both?">
    A **requester** farms out: sends reservations to GNet and receives status updates back. A **provider** farms in: hosts an endpoint that GNet posts trips to, and pushes status updates back as the trip progresses.

    A single company can be both, and most dispatch platforms eventually are. Build them as two separate integrations.
  </Accordion>

  <Accordion title="We are a multi-tenant dispatch platform. Does each tenant need its own GNet account?">
    Yes — and this is the part worth reading twice, because accounts and credentials are not the same thing.

    **Each tenant gets its own GNet account and its own GRiDD ID.** The GRiDD ID is what identifies the transportation company and routes the reservation, and partnerships are established per GRiDD ID, not at the platform level. Your tenants' partner relationships belong to them.

    **Your platform gets one single credential.** You do not collect or store a credential per tenant. GNet issues one master credential to your central system, and you act on behalf of any of your tenants by setting the GRiDD IDs on each payload:

    ```json theme={null}
    "affiliateReservation": {
      "action": "NEW",
      "requesterId": "<the tenant you are sending on behalf of>",
      "providerId": "<the partner receiving the trip>",
      "requesterResNo": "<that tenant's reservation number>"
    }
    ```

    This is the software vendor model, the same one established dispatch platforms on the network use.
  </Accordion>

  <Accordion title="How do we route an incoming trip to the right tenant?">
    On **`affiliateReservation.providerId`** — the GRiDD ID of the operator the trip is for. You expose one adapter URL for the whole platform and route on that field.

    <Warning>
      Do not route on `account.accountNumber`. That field answers a different question: **which customer of yours is the trip for**, expressed as the customer ID in *your* system. It is not the sender's GNet ID and it is not a tenant key — a broker or TMC will set it to their end client's account with you. Treating it as the sender is the most common thing certification sends integrators back to fix. See [Account number: who the trip is for](/platform-api/receiving-reservations#account-number-who-the-trip-is-for).
    </Warning>

    On the send side the same pair identifies the tenant: set `requesterId` to the operator you are sending on behalf of, and `providerId` to the receiving affiliate.
  </Accordion>

  <Accordion title="Can we manage tenant signup and partnerships inside our own product?">
    Yes. You do not have to send tenants to the Connect portal.

    * [Signup](/connect-api/sign-up) registers a new company from your own UI.
    * [Manage Partners](/connect-api/manage-partners) lets a tenant view, accept, or reject incoming partnership requests.
    * [Get Partners](/connect-api/get-partners) lists a tenant's existing partners.

    <Warning>
      Signup runs an AI verification check. If content looks fake, malformed or spammy it does not auto-approve — it is flagged for manual review, and a score below threshold returns **HTTP 428**. Show a "pending GRiDD review" state after submission. Never confirm activation until the GRiDD ID is actually active.
    </Warning>
  </Accordion>
</AccordionGroup>

## Access and environments

<AccordionGroup>
  <Accordion title="What are the test and production endpoints?">
    | Environment      | Base URL                                  |
    | ---------------- | ----------------------------------------- |
    | Test             | `https://gtest.grdd.net/platform.svc`     |
    | Production       | `https://gnet.grdd.net/platform.svc`      |
    | Location service | `https://location.grdd.net/api/GGPS.svc/` |

    Live endpoint help is at [`gtest.grdd.net/platform.svc/help`](https://gtest.grdd.net/platform.svc/help). The current API version is **V1** — every endpoint takes a `version` parameter of `V1`.
  </Accordion>

  <Accordion title="How do we get test credentials, and is there a sandbox?">
    There is **no isolated sandbox environment**. Test trips on GNet are real GNet messages, sent from GNet's `gnettest` account and cancelled afterwards.

    What you get instead is a test account to develop against, provisioned by the GNet team — contact us with your company details and intended direction (Farm In, Farm Out, or both).

    To rehearse before certification, use the **GNet Robot** in the [Connect dashboard](https://dashboard.grdd.net/dashboard/integration-robot). It runs the same exchanges certification does, on demand, and tells you exactly which status call it is waiting for at each step. See [Certification](/gnet-platform/certification).
  </Accordion>

  <Accordion title="Our test trips never reach our adapter. Why?">
    Almost always because the test operator is not linked to your production operator as its software provider. That link is a backend configuration on our side and cannot be set through the Connect portal.

    Send us the test operator ID and the production operator it should be linked to, and we will configure it. This is also a required step in any backing-system migration, not just new integrations.
  </Accordion>
</AccordionGroup>

## Authentication

<AccordionGroup>
  <Accordion title="How does authentication work?">
    Two schemes, depending on direction.

    **Calls you make to GNet** use a token. POST to [`getToken2`](/platform-api/authentication/get-token) with your `uid` and `pw`, then send the token on every subsequent call:

    ```http theme={null}
    token: <token_value>
    Content-Type: application/json
    ```

    <Warning>
      The header is named `token`, lowercase. It is **not** `Authorization: Bearer`. This trips up almost every integrator who assumes OAuth2 conventions.
    </Warning>

    **Calls GNet makes to you** use HTTP Basic Auth with an `api_key` and `api_secret` we issue alongside your credentials:

    ```http theme={null}
    Authorization: Basic base64(api_key:api_secret)
    ```

    Validate that header on every inbound request to your Farm In endpoint.
  </Accordion>

  <Accordion title="How long do tokens last?">
    A token stays active until it is explicitly released — there is no timer expiry. Call [`releaseToken`](/platform-api/authentication/release-token) at the end of a session to invalidate it.

    The implication matters: a leaked token remains valid indefinitely until released. Treat tokens as secrets, keep them server-side, and never hard-code credentials.
  </Accordion>

  <Accordion title="How often should we fetch a token?">
    Once at startup. Cache it, reuse it on every call, and re-authenticate only when you get a `401`.

    <Warning>
      Do not call `getToken2` per request. Repeated logins get throttled.
    </Warning>
  </Accordion>

  <Accordion title="We get a 400 &#x22;InvalidToken / Not Authorized&#x22; when creating a token.">
    That error almost always means **no API Gateway account exists** for the company — not that the credentials are wrong.

    A Gateway (API) account is separate from a Connect dashboard login. Having one does not give you the other. If you hit this, contact us to have the Gateway user created.
  </Accordion>

  <Accordion title="Do your webhooks include a signature or shared secret?">
    There is no HMAC payload signature. Inbound calls from GNet are authenticated with **HTTP Basic Auth** using the `api_key` and `api_secret` issued to you, over HTTPS.

    Validate the Basic Auth header on every request and reject anything without it.
  </Accordion>
</AccordionGroup>

## Webhooks

<AccordionGroup>
  <Accordion title="How many endpoints do we need to expose?">
    **Two.** Not one per event type — this is the most common misreading of the spec.

    <Steps>
      <Step title="Farm In listener">
        Receives new trips, updates and cancellations. One endpoint handles all three; read `affiliateReservation.action` (`NEW`, `UPDATE`, `CANCEL`) to decide what to do.
      </Step>

      <Step title="Status callback listener">
        Receives status updates on trips you farmed out, plus driver info, vehicle info, and closing costs at trip close.
      </Step>
    </Steps>

    There is no separate endpoint for cancellations, affiliate responses, or GPS. Cancellations arrive on the Farm In listener. GPS is not pushed at all — see [Tracking](#tracking) below.
  </Accordion>

  <Accordion title="How do we register or change our webhook URLs?">
    Both URLs are self-service from the [Connect dashboard](https://connect.grdd.net/dashboard). You do not need to contact support to change them.

    <Warning>
      Register **both**. Setting the Farm In URL and forgetting the status callback URL is the single most common integration mistake we see — trips arrive correctly and then status updates silently go nowhere.
    </Warning>
  </Accordion>

  <Accordion title="How are callbacks delivered? Can we rely on ordering?">
    No. Callbacks are **fire-and-forget, with no retries**, and a quick burst — status, then driver, then vehicle — can arrive out of order.

    Two rules follow:

    * **Never move a trip's state backwards** on a late callback.
    * If you suspect you missed one, reconcile with [Get Reservation by Transaction ID](/platform-api/reservations/get-by-transaction-id) — as a one-off reconciliation, not on a timer.

    A failed delivery marks the transaction `FAILED` and notifies the sender, who can resubmit.
  </Accordion>

  <Accordion title="How do we decline a trip we cannot cover?">
    Respond with `success: false` and a clear message explaining why. An explicit decline is a valid, expected answer — a trip that is acknowledged but never acted on is not.

    QUOTE requests are answered synchronously back to the sender the same way.
  </Accordion>

  <Accordion title="Do you call from a fixed IP range we can allowlist?">
    No. There is no fixed IP range. Every call to your endpoint carries your Basic Auth key and secret — authenticate on that.
  </Accordion>

  <Accordion title="What does our Farm In endpoint have to return?">
    A synchronous response. GNet confirms the booking at request time, not asynchronously.

    ```json theme={null}
    {
      "success": true,
      "reservationId": "11545-001",
      "totalAmount": "130.67",
      "transactionId": "<echoed from the request>"
    }
    ```

    On rejection:

    ```json theme={null}
    {
      "success": false,
      "message": "Unable to process the request due to XYZ.",
      "transactionId": "<echoed from the request>"
    }
    ```

    The endpoint must also answer a `GET` with `{"success": true}` for our health check, accept `Content-Type: application/json`, and be reachable over public HTTPS.
  </Accordion>
</AccordionGroup>

## Sending trips

<AccordionGroup>
  <Accordion title="What fields are required for sendTrip?">
    At minimum:

    | Field                                 | Notes                                                      |
    | ------------------------------------- | ---------------------------------------------------------- |
    | `affiliateReservation.action`         | `NEW`, `UPDATE` or `CANCEL`                                |
    | `affiliateReservation.requesterId`    | GRiDD ID of the sender                                     |
    | `affiliateReservation.providerId`     | GRiDD ID of the receiver                                   |
    | `affiliateReservation.requesterResNo` | Sender's reservation number                                |
    | `affiliateReservation.providerResNo`  | Required on `UPDATE` and `CANCEL` only                     |
    | `transactionId`                       | Blank on `NEW`, required on every update                   |
    | `locations.pickup`                    | `time`, `locationType`, and address fields                 |
    | `locations.dropOff`                   | `locationType` and address fields                          |
    | `passengerCount`                      | At least one passenger                                     |
    | `passengers[].firstName` / `lastName` | Phone strongly recommended                                 |
    | `preferredVehicleType`                | See [Vehicle Types](/platform-api/reference/vehicle-types) |
    | `reservationType`                     | `REGULAR`, `ONDEMAND` or `QUOTE`                           |

    Full reference: [Object Model](/platform-api/reference/object-model).
  </Accordion>

  <Accordion title="How are dates and times formatted?">
    `YYYY-MM-DDTHH:mm:ss`, for example `2026-08-08T19:00:00`.

    Pickup, drop-off and stop times are **local to the pickup location and carry no timezone**. Do not convert to UTC and do not append an offset.
  </Accordion>

  <Accordion title="How do we format airport trips?">
    Set `locationType` to `AIRPORT` and use IATA codes — without them, airport trips cannot be interpreted.

    * 3-character IATA airport code goes in the `address` field
    * 2-character IATA airline code goes in `airlineCode`
    * Put the real flight number in `flightNumber`, or leave it empty. Never send `TBD` or a placeholder.
    * For an FBO, set `FBO` to `True` on that leg. Airline code `00` is also an FBO indicator.
  </Accordion>

  <Accordion title="How do we make our integration idempotent?">
    Dedupe on **`transactionId`**. It is assigned by GNet and stays the same through `UPDATE` and `CANCEL` for the life of the trip.

    * If a `NEW` arrives carrying a `transactionId` you already hold, return the existing `reservationId` rather than creating a second booking.
    * On the send side, `sendTrip` keeps one reservation per `requesterResNo` per sender, so a resent `NEW` returns the same transaction instead of booking twice.
  </Accordion>

  <Accordion title="What is the difference between runType and preferredVehicleType?">
    `preferredVehicleType` is the vehicle — see [Vehicle Types](/platform-api/reference/vehicle-types). `runType` is the service: `TRANSPORT`, `AIRPORT` or `HOURLY`.

    For an hourly trip, also set `totalTripDuration` in **minutes**.
  </Accordion>

  <Accordion title="How should vehicle types be mapped?">
    It depends on direction, and only one direction is your code's problem.

    **Farming out.** Send one of GNet's standard [vehicle type codes](/platform-api/reference/vehicle-types) in `preferredVehicleType`. GNet performs the mapping into the target system. You do not need to know what the receiving system calls its vehicles.

    **Farming in.** Configure the mapping in the GNet portal, per company, per GRiDD ID — each GNet vehicle type maps to the vehicle type you use internally. By the time a payload reaches your system, the vehicle type has already been mapped.

    <Note>
      Receive-side mapping is portal configuration, not code. Do not build a translation table in your adapter for inbound trips. The mapping menu sits on each partner page and is enabled per integration — if you do not see it, ask us to turn it on for your account.
    </Note>
  </Accordion>
</AccordionGroup>

## Quotes and bidding

<AccordionGroup>
  <Accordion title="How do we request a price without booking?">
    Send `reservationType: "QUOTE"` on the same endpoint. On the receiving side, return the price but **do not create a reservation record** — a quote is a price check, and the requester may never follow up.
  </Accordion>

  <Accordion title="What if we do not want to price quotes at all?">
    Quotes are optional. If a QUOTE arrives and you do not price it, return the normal success response with an empty `totalAmount` and do not create a record.
  </Accordion>

  <Accordion title="Our quotes return a result but the rate never comes through.">
    Almost always a response format violation. Three rules, all strict:

    ```json theme={null}
    {
      "success": true,
      "fees": [],
      "totalAmount": "101.47",
      "transactionId": "68c73da7-eaa0-437c-b77b-62699c895499"
    }
    ```

    * `totalAmount` **must be a JSON string** — `"101.47"`, not `101.47`
    * `fees` **must be present as an array**, even when empty
    * **Do not include a `message` field** in a success response — it breaks response parsing
    * `transactionId` must echo the exact value from the request

    Any one of these silently breaks the rate lookup. Compare your actual response against this shape before reporting a bug.
  </Accordion>

  <Accordion title="We quoted several partners and picked a winner. Do we cancel the losers?">
    No. Proceed with the winner; GNet handles cleanup of unaccepted QUOTE transactions. Partners are not left hanging.

    If you are building multi-partner bidding as a core workflow, ask us about **Farm House** — you broadcast a request to a target market, partners bid, and you pick the winner. It fits that pattern better than QUOTE-then-confirm.
  </Accordion>
</AccordionGroup>

## Tracking

<AccordionGroup>
  <Accordion title="How do we get driver location?">
    GPS is **pull, not push**. It does not arrive on a webhook.

    ```http theme={null}
    GET https://location.grdd.net/api/GGPS.svc/GetLocationByRes/{providerId}/{providerResNo}/v1
    ```

    Use the **provider's** GRiDD ID and the **provider's** reservation number — do not mix one side's ID with the other side's reservation number. See [Location by Reservation](/platform-api/tracking/location-by-resno), or fetch several at once with [Location for Multiple Reservations](/platform-api/tracking/location-by-resno-array).
  </Accordion>

  <Accordion title="We are the provider — how do we push GPS to GNet?">
    Push positions with `saveGPScache`, or batch several drivers into one call with `GPSgrab`.

    | Rule                                | Value                           |
    | ----------------------------------- | ------------------------------- |
    | Maximum call rate                   | 1 per second                    |
    | Position expiry                     | 120 seconds after the last push |
    | Position expiry while `ON_LOCATION` | 720 seconds                     |

    Keep pushes under two minutes apart or the position goes stale. These calls take the `token` header only — **do not add a Basic Auth header**.

    <Note>
      A `403` on `saveGPScache` usually means a bad `internalBookingId`, not a permissions problem. Send the booking ID from the GNet payload, not your own internal reference.
    </Note>

    GPS is optional for certification, but recommended — buyers increasingly filter on it for duty of care.
  </Accordion>

  <Accordion title="The first GPS call comes back empty.">
    Expected behaviour with some dispatch systems — the first call triggers a background fetch of the coordinates.

    Wait 3–5 seconds and retry. Build in two or three attempts before surfacing "location unavailable" to an end user. Do not treat an empty first response as a failure.
  </Accordion>

  <Accordion title="Why is GPS available for some trips and not others?">
    Availability depends on whether that affiliate's dispatch system is actively sharing location for that reservation. It is per-reservation and per-partner, not a blanket account setting. Check visually against the [live map](https://connect.grdd.net/dashboard/livemap) when debugging.
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="A trip failed to deliver. What now?">
    When transmission fails, `affiliateReservation.status` is set to `FAILED` and `affiliateReservation.notes` carries the reason. Alert on that combination.

    Transactions can fail for infrastructure reasons unrelated to your payload — a target gateway down for maintenance, for example. Use [Retry Transaction](/platform-api/reservations/retry) rather than resubmitting as a new trip, which would create a duplicate.
  </Accordion>

  <Accordion title="We got &#x22;Invalid Affiliate Integration.&#x22;">
    The two companies do not have an established partnership, or the receiving side is not configured to accept from the sender. Confirm the partnership exists in Connect before debugging the payload — this is a relationship error, not a data error.
  </Accordion>

  <Accordion title="Are amounts final at CLOSE?">
    Treat them as final at `CLOSE`, but be able to accept a correction — a provider can resend `CLOSE` with revised amounts.

    Note also that `FAILED` is **not a trip state**. It marks a failed transmission between systems; the trip itself is unaffected.
  </Accordion>

  <Accordion title="Should we poll for reservation status?">
    No. Use the status callback webhook.

    <Warning>
      Do not call `getReservationByTransactionId` or `getReservationByReservationId` in a loop or on a timer. Those endpoints are for one-off snapshots and reconciliation. Polling them is what webhooks exist to prevent.
    </Warning>
  </Accordion>
</AccordionGroup>

## Migrating an existing member

<AccordionGroup>
  <Accordion title="A member is moving to our platform from another dispatch system. Do they need a new GNet account?">
    No — and opening a second production account is the wrong move.

    Repoint the existing account instead. The existing GRiDD ID keeps every partnership already established, the partner ID already issued to counterparties stays valid, the change is invisible to those counterparties, and rollback is cheap. A parallel account means re-establishing every partnership from scratch.

    Sequence: build and certify against a sandbox, then repoint the live account, then re-register the webhook URLs. Tell us so the software provider link can be updated on our side.
  </Accordion>
</AccordionGroup>

## Still stuck?

<Card title="Talk to the integration team" icon="envelope" href="mailto:support@grdd.net">
  Send the payload you sent, the response you got, and the transaction ID. That is almost always enough for us to find it in the adapter logs on the first pass.
</Card>
