GraphQL API

Query and mutate dealership customer data, vehicle inventory, and dealer information through a standard GraphQL endpoint.

Endpoint

POST https://gql.space-auto.com/graphql

Authentication

Pass your API key in the Authorization header. Read-only keys can run queries; read+write keys can also run mutations.

Header
Authorization: YOUR_API_KEY

Queries

searchCustomers

Search customers with pagination, filtering, and sorting. Returns customer profiles with all related data (deals, appointments, trade-ins, credit applications, references, activity, labels, favorites).

FilterTypeDescription
searchStringFull-text search across name, email, phone, address
unseenOnlyBooleanOnly customers with unread events
minDate / maxDateStringCreated-at date range (ISO 8601)
minUpdatedAt / maxUpdatedAtStringUpdated-at date range (ISO 8601)
includeIds / excludeIds[ID]Include or exclude specific customer IDs
dealerIds[ID]Filter by assigned dealer
labels[String]Filter by customer labels

Sort fields: firstName, lastName, email, phone, createdAt, updatedAt. Pagination: skip + limit (max 100).

curl
curl -X POST https://gql.space-auto.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: YOUR_API_KEY" \
  -d '{
  "query": "query ($input: SearchCustomersInput!) { searchCustomers(input: $input) { total customers { id firstName lastName email phone deals { vin paymentType } appointments { type status appointmentAt } } } }",
  "variables": {
    "input": {
      "pagination": { "limit": 10, "skip": 0 },
      "sort": { "field": "updatedAt", "order": "desc" },
      "filters": { "search": "gmail.com" }
    }
  }
}'
TypeScript
const ENDPOINT = "https://gql.space-auto.com/graphql";
const API_KEY = "YOUR_API_KEY";

interface SearchCustomersResponse {
  data: {
    searchCustomers: {
      total: number;
      customers: Array<{
        id: string;
        firstName: string;
        lastName: string;
        email: string;
        phone: string;
      }>;
    };
  };
}

async function searchCustomers(search: string, limit = 10, skip = 0) {
  const res = await fetch(ENDPOINT, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: API_KEY,
    },
    body: JSON.stringify({
      query: `query ($input: SearchCustomersInput!) {
        searchCustomers(input: $input) {
          total
          customers { id firstName lastName email phone }
        }
      }`,
      variables: {
        input: {
          pagination: { limit, skip },
          sort: { field: "updatedAt", order: "desc" },
          filters: { search },
        },
      },
    }),
  });
  return (await res.json()) as SearchCustomersResponse;
}

searchVehicles

Search vehicle inventory with rich filtering: make/model/trim hierarchy, condition, price/year/mileage ranges, body style, fuel, transmission, drivetrain, features, and more.

curl
curl -X POST https://gql.space-auto.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: YOUR_API_KEY" \
  -d '{
  "query": "query ($input: SearchVehiclesInput!) { searchVehicles(input: $input) { total vehicles { vin year make model trim condition price mileage status photos } } }",
  "variables": {
    "input": {
      "pagination": { "limit": 10 },
      "sort": { "field": "price", "order": "asc" },
      "filters": {
        "condition": ["new"],
        "make": [{ "value": "Toyota", "model": [{ "value": "Camry" }] }],
        "price": { "from": 20000, "to": 40000 }
      }
    }
  }
}'
TypeScript
async function searchVehicles(filters: Record<string, any>, limit = 10) {
  const res = await fetch(ENDPOINT, {
    method: "POST",
    headers: { "Content-Type": "application/json", Authorization: API_KEY },
    body: JSON.stringify({
      query: `query ($input: SearchVehiclesInput!) {
        searchVehicles(input: $input) {
          total
          vehicles { vin year make model trim condition price mileage status photos }
        }
      }`,
      variables: { input: { pagination: { limit }, filters } },
    }),
  });
  return res.json();
}

dealers

List all dealers (salespeople) for the authorized dealership. No input required.

curl
curl -X POST https://gql.space-auto.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: YOUR_API_KEY" \
  -d '{ "query": "{ dealers { id email firstName lastName phone jobTitle department isWorking isAutoAssignmentEnabled locations } }" }'

Mutations

All mutations require write permission. Each mutation accepts a customer field for identification (provide at least one of id, email, or phone) plus optional profile fields. Two assignment-related fields apply consistently across all mutations:

  • customer.location — optional; if it matches a configured dealership location (case-sensitive), it is appended to the customer's locations array. Unknown names are silently ignored.
  • customer.leadSource — optional; applied only when creating a new customer. Never overwrites an existing customer's lead source.
  • label (top-level) — optional; tags the customer with a dealership label (matched case-insensitively; auto-created if it does not exist).

submitLead

Create or update a customer lead, optionally associating a vehicle deal.

VIN deduplication: if the customer already has a deal for the supplied VIN, no new deal is created (the mutation still succeeds and returns the customer with the existing deal).

curl
curl -X POST https://gql.space-auto.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: YOUR_API_KEY" \
  -d '{
  "query": "mutation ($input: SubmitLeadInput!) { submitLead(input: $input) { id firstName lastName email phone deals { vin paymentType } labels { name color } } }",
  "variables": {
    "input": {
      "customer": {
        "email": "jane@example.com",
        "firstName": "Jane",
        "lastName": "Doe",
        "phone": "+15551234567"
      },
      "deal": {
        "vin": "1HGCG5655WA041389",
        "paymentType": "financing",
        "notes": "Interested in the 2024 model"
      },
      "label": "Hot Lead"
    }
  }
}'

submitAppointment

Schedule a customer appointment.

  • type (AppointmentType): sales, service, delivery, other
  • status (AppointmentStatus): set, confirmed, declined, completed, missed, unresolved
curl
curl -X POST https://gql.space-auto.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: YOUR_API_KEY" \
  -d '{
  "query": "mutation ($input: SubmitAppointmentInput!) { submitAppointment(input: $input) { id firstName lastName appointments { id type status appointmentAt } } }",
  "variables": {
    "input": {
      "customer": { "email": "jane@example.com" },
      "appointment": {
        "type": "sales",
        "status": "set",
        "appointmentAt": "2025-03-15T14:00:00Z",
        "vin": "1HGCG5655WA041389",
        "notes": "Customer prefers afternoon"
      },
      "label": "Follow-up"
    }
  }
}'

submitLog

Create a communication log (meeting, text, email, phone) or an internal note for a customer. All entries are attributed to the dealership's crm@space.auto system dealer.

  • type (SubmitLogType): meeting, text, email, phone (communication logs) or note (internal, dealer-only note).
  • body (required): main text content.
  • transcription and summary (optional): appended to body as separate Transcription: ... / Summary: ... sections.
  • datetime (optional ISO 8601): backdate a log entry. Ignored for notes.
  • subject (optional): stored separately on the activity data.

When to use which type: pick meeting/text/email/phone when recording an actual customer interaction so it surfaces in the dealership activity feed with the right filter. Pick note for internal observations, reminders, or context not tied to a customer-facing communication.

curl — communication log
curl -X POST https://gql.space-auto.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: YOUR_API_KEY" \
  -d '{
  "query": "mutation ($input: SubmitLogInput!) { submitLog(input: $input) { id firstName lastName activity { id type action createdAt data } } }",
  "variables": {
    "input": {
      "customer": { "email": "jane@example.com" },
      "log": {
        "type": "phone",
        "subject": "Follow-up on 2024 RAV4",
        "body": "Customer called to ask about financing options.",
        "datetime": "2025-03-15T14:00:00Z",
        "transcription": "Agent: Hi Jane... Jane: Hi, I was wondering...",
        "summary": "Ready to schedule a test drive next week."
      }
    }
  }
}'
curl — internal note
curl -X POST https://gql.space-auto.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: YOUR_API_KEY" \
  -d '{
  "query": "mutation ($input: SubmitLogInput!) { submitLog(input: $input) { id firstName lastName activity { id type action createdAt data } } }",
  "variables": {
    "input": {
      "customer": { "email": "jane@example.com" },
      "log": {
        "type": "note",
        "body": "Customer prefers afternoon appointments. Works night shifts."
      }
    }
  }
}'
TypeScript
type SubmitLogType = "meeting" | "text" | "email" | "phone" | "note";

async function submitLog(params: {
  customerEmail?: string;
  customerPhone?: string;
  customerId?: string;
  type: SubmitLogType;
  body: string;
  subject?: string;
  datetime?: string;
  transcription?: string;
  summary?: string;
  label?: string;
}) {
  const { customerEmail, customerPhone, customerId, label, ...log } = params;
  const res = await fetch(ENDPOINT, {
    method: "POST",
    headers: { "Content-Type": "application/json", Authorization: API_KEY },
    body: JSON.stringify({
      query: `mutation ($input: SubmitLogInput!) {
        submitLog(input: $input) {
          id firstName lastName
          activity { id type action createdAt data }
        }
      }`,
      variables: {
        input: {
          customer: { id: customerId, email: customerEmail, phone: customerPhone },
          log,
          ...(label ? { label } : {}),
        },
      },
    }),
  });
  return res.json();
}

Other Mutations

MutationDescriptionKey Input Fields
submitCreditApplicationSubmit a credit application with personal, employment, and financial infotype ("primary" | "co-signer"), ssn, birthDate, applicantType, homeOwnership, employmentStatus, monthlyIncome
submitTradeinRecord a trade-in vehicle with valuation datavin, year, make, model, mileage, condition (excellent | good | fair), minTrade, maxTrade, payoffType (neither | loan | lease), payoffAmount
submitReferenceAdd a personal/professional referencefirstName, lastName, phone, relationship
submitLogLog a customer communication or create an internal note (attributed to crm@space.auto)type (meeting|text|email|phone|note), body, subject, datetime, transcription, summary

Customer Labels (label)

Every mutation accepts an optional top-level label: String. When provided:

  1. A dealership label with the given name is resolved case-insensitively (so "VIP", "vip", and "Vip" all match the same label).
  2. If no matching dealership label exists, a new one is created (default color: grey).
  3. The label is assigned to the customer if not already assigned. If the customer already has the label, the operation is a no-op.

Returned labels { name color } selection reflects the customer's full set of labels after the mutation.

Customer location and leadSource

The shared customer object on every mutation accepts two additional optional fields that govern customer assignment:

  • location: String — dealership location name. Validated against the dealership's configured locations (case-sensitive exact match on the name). If it matches, the name is appended to the customer's locations array (deduplicated). If it does not match, the value is silently ignored — the mutation still succeeds. Applies to both new and existing customers.
  • leadSource: String — free-form lead-source label. Only applied when a new customer is created. For existing customers this field is ignored to preserve the original attribution.

Enum Reference

EnumValues
DealPaymentTypecash, leasing, financing
DealSourceweb, phone, walk, other
DealProcessingStatusfresh, desking, appointment, closed, archived
AppointmentTypesales, service, delivery, other
AppointmentStatusset, confirmed, declined, completed, missed, unresolved
TradeinConditionexcellent, good, fair
TradeinPayoffTypeneither, loan, lease
ApplicantTypeself, spouse, relative, registeredDomesticPartner, civilUnion, other
HomeOwnershiprent, mortgage, own, other
EmploymentStatusemployed, unemployed, retired, selfemployed, other
VehicleConditionnew, used, certified
VehicleStatusavailable, unavailable, hidden
SubmitLogTypemeeting, text, email, phone, note

Error Handling

GraphQL errors are returned in the standard errors array. HTTP status is always 200 for valid GraphQL requests. Check data and errors in the response body.

Real-time events via AppSync Events WebSocket. Events stream when customers, deals, appointments, or other records change. Channel format: /space/{dealershipId}/events. Uses the same API key for authentication.
Disconnected
Event Log

Real-time Subscriptions

Receive real-time event notifications via AWS AppSync Events WebSocket. Events are pushed when any data changes — customer updates, new leads, deal status changes, appointments, messages, and more.

Endpoint

WebSocket: wss://gql-events.space-auto.com/event/realtime

Subprotocol: aws-appsync-event-ws

Authentication

Auth is passed as a WebSocket subprotocol. Base64URL-encode a JSON auth object and prefix with header-:

Subprotocol Format
# 1. Build auth JSON
{ "host": "gql-events.space-auto.com", "Authorization": "YOUR_API_KEY" }

# 2. Base64URL encode (replace + with -, / with _, strip = padding)
# 3. Prefix with "header-"

# 4. Pass as second WebSocket subprotocol alongside "aws-appsync-event-ws"

Channel Format

Events are published to channels scoped by dealership:

/space/{dealershipId}/events

Your API key authorizes access only to the dealership it belongs to. Subscribing to a different dealership's channel will be rejected.

Protocol Flow

StepDirectionMessage
1. ConnectOpen WebSocket with subprotocols: aws-appsync-event-ws + header-{base64url_auth}
2. InitClient → Server{ "type": "connection_init" }
3. AckServer → Client{ "type": "connection_ack", "connectionTimeoutMs": 300000 }
4. SubscribeClient → Server{ "type": "subscribe", "id": "uuid", "channel": "/space/{id}/events", "authorization": { "host": "...", "Authorization": "..." } }
5. SubscribedServer → Client{ "type": "subscribe_success", "id": "uuid" }
6. EventsServer → Client{ "type": "data", "id": "uuid", "event": "{...}" }
7. KeepaliveServer → Client{ "type": "ka" } — server sends periodically; close if not received within connectionTimeoutMs
8. UnsubscribeClient → Server{ "type": "unsubscribe", "id": "uuid" }

Event Payload

Each data message contains a JSON-stringified event in the event field. Common event properties:

FieldDescription
typeEntity type: customer, deal, email, sms, call, internal, tradeins, payments, referents, external-lead, documents, appointments
actionWhat happened: create, update, merge, delete, send
dealershipIdUUID of the dealership
actorType"customer" or "dealer"
actorIdUUID of the actor

Complete Example

TypeScript
const EVENTS_HOST = "gql-events.space-auto.com";
const API_KEY = "YOUR_API_KEY";
const DEALERSHIP_ID = "YOUR_DEALERSHIP_ID";

function base64url(obj: object): string {
  return btoa(JSON.stringify(obj))
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

function connect() {
  const authProtocol =
    "header-" + base64url({ host: EVENTS_HOST, Authorization: API_KEY });

  const ws = new WebSocket(
    `wss://${EVENTS_HOST}/event/realtime`,
    ["aws-appsync-event-ws", authProtocol],
  );

  ws.onopen = () => {
    console.log("WebSocket opened, sending connection_init");
    ws.send(JSON.stringify({ type: "connection_init" }));
  };

  ws.onmessage = (evt) => {
    const msg = JSON.parse(evt.data as string);

    if (msg.type === "connection_ack") {
      console.log("Connected, timeout:", msg.connectionTimeoutMs);
      ws.send(JSON.stringify({
        type: "subscribe",
        id: crypto.randomUUID(),
        channel: `/space/${DEALERSHIP_ID}/events`,
        authorization: { host: EVENTS_HOST, Authorization: API_KEY },
      }));
    }

    if (msg.type === "subscribe_success") {
      console.log("Subscribed to channel");
    }

    if (msg.type === "data") {
      const event = JSON.parse(msg.event);
      console.log("Event:", event.type, event.action, event);
    }
  };

  ws.onerror = (e) => console.error("WebSocket error", e);
  ws.onclose = () => {
    console.log("Disconnected — reconnect after delay");
    setTimeout(connect, 3000);
  };
}

connect();

Reconnection

Connections close after the timeout if keepalive is missed, or on network issues. Implement exponential backoff for reconnection (e.g., 1s, 2s, 4s, max 30s). Always re-subscribe after reconnecting.

Model Context Protocol (MCP) endpoint for AI agent integration. Send JSON-RPC 2.0 requests to /mcp. Authenticate with Authorization: Bearer <api_key>.
Request
Response

      

MCP Server

Model Context Protocol endpoint for AI agent integration. Implements JSON-RPC 2.0 over HTTP, compatible with Claude, Cursor, and other MCP clients.

Endpoint

POST https://gql.space-auto.com/mcp

Authentication

All methods except ping require an API key:

Header
Authorization: Bearer YOUR_API_KEY

Note the Bearer prefix — this differs from the GraphQL API which uses the raw key.

Protocol

All requests use JSON-RPC 2.0 format:

JSON-RPC 2.0 Request
{ "jsonrpc": "2.0", "id": 1, "method": "METHOD_NAME", "params": { ... } }

Methods

initialize

Initialize the MCP session. Returns server capabilities, name, and instructions.

curl
curl -X POST https://gql.space-auto.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": { "name": "my-client", "version": "1.0.0" }
  }
}'

tools/list

List available tools. Tools requiring write permission are hidden if your key is read-only.

curl
curl -X POST https://gql.space-auto.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }'

tools/call

Execute a tool by name with arguments. Returns the tool result as text content.

curl
curl -X POST https://gql.space-auto.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "search_customers",
    "arguments": { "search": "gmail.com", "limit": 5 }
  }
}'
TypeScript
const MCP_ENDPOINT = "https://gql.space-auto.com/mcp";
const API_KEY = "YOUR_API_KEY";

let requestId = 0;

async function mcpRequest(method: string, params?: Record<string, any>) {
  const res = await fetch(MCP_ENDPOINT, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${API_KEY}`,
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: ++requestId,
      method,
      params,
    }),
  });
  return res.json();
}

// Initialize session
await mcpRequest("initialize", {
  protocolVersion: "2024-11-05",
  capabilities: {},
  clientInfo: { name: "my-app", version: "1.0.0" },
});

// List tools
const tools = await mcpRequest("tools/list");

// Call a tool
const result = await mcpRequest("tools/call", {
  name: "search_customers",
  arguments: { search: "gmail.com", limit: 5 },
});

// Read a resource
const guide = await mcpRequest("resources/read", {
  uri: "docs://api-guide",
});

Available Tools

ToolPermissionDescription
search_customersReadSearch customers with filtering, sorting, and pagination
search_vehiclesReadSearch vehicle inventory with rich filters
fetch_dealersReadList dealers with contact info and status
submit_leadWriteCreate or update a customer lead with optional deal
submit_credit_applicationWriteSubmit a credit application
submit_tradeinWriteRecord a trade-in vehicle
submit_referenceWriteAdd a reference for credit verification
submit_appointmentWriteSchedule a customer appointment
submit_logWriteLog a customer communication (meeting|text|email|phone) or create an internal note

Resources

URINameDescription
schema://graphqlGraphQL SchemaFull GraphQL SDL schema
docs://api-guideAPI Usage GuideComprehensive API documentation (Markdown)

resources/read

curl
curl -X POST https://gql.space-auto.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{ "jsonrpc": "2.0", "id": 1, "method": "resources/read", "params": { "uri": "docs://api-guide" } }'

Error Codes

CodeMeaning
-32700Parse error (invalid JSON)
-32600Invalid request (missing method)
-32601Method not found
-32602Invalid params / tool execution failed
-32603Internal error
-1001Unauthorized (missing or invalid API key)
-1002Forbidden (insufficient permissions)

SKILL.MD

LLM-optimized integration reference for all Space Auto External API services. Copy and provide to any AI assistant for complete API access.

Overview

You have access to the Space Auto External API — a dealership data platform with three integration interfaces. All services share the same API key and are scoped to a single dealership.

ServiceEndpointAuth HeaderUse When
GraphQL APIPOST https://gql.space-auto.com/graphqlAuthorization: {key}Structured queries/mutations with field selection
Events WebSocketwss://gql-events.space-auto.com/event/realtimeBase64 in URL paramsReal-time change notifications
MCP ServerPOST https://gql.space-auto.com/mcpAuthorization: Bearer {key}AI agent tool calling (JSON-RPC 2.0)

Authentication

A single API key grants access to all services. Permission levels:

  • Read: searchCustomers, searchVehicles, dealers, all MCP read tools
  • Read + Write: All of the above plus submitLead, submitAppointment, submitCreditApplication, submitTradein, submitReference, submitLog, and corresponding MCP write tools
GraphQL uses Authorization: {key} (raw). MCP uses Authorization: Bearer {key} (with Bearer prefix). Events use base64-encoded headers in the WebSocket URL.

Service 1: GraphQL API

Endpoint: POST https://gql.space-auto.com/graphql
Content-Type: application/json
Auth: Authorization: {api_key}

Query: searchCustomers

Search customers with pagination, filtering, and sorting. Returns profiles with deals, appointments, trade-ins, credit applications, references, activity, labels, favorites.

Input: SearchCustomersInput with optional pagination (skip, limit max 100), sort (field: firstName|lastName|email|phone|createdAt|updatedAt, order: asc|desc), filters (search: string, unseenOnly: bool, minDate/maxDate: ISO string, includeIds/excludeIds: [ID], dealerIds: [ID], labels: [String]).

Query: searchVehicles

Search vehicle inventory. Input: SearchVehiclesInput with optional pagination, sort (field: price|mileage|year|mpg|views|likes|make|model|inventoryDate|updatedAt|stock, order: asc|desc), filters (condition: [VehicleCondition!] (new|used|certified), status: [VehicleStatus!] (available|unavailable|hidden), make: [{value, model: [{value, trim}]}], body/fuel/transmission/driveline/color: [String], year/price/mileage: {from, to}, vins: [String], substatus: [String], feature filters: safetyFeatures/technologyFeatures/exteriorFeatures/interiorFeatures/mechanicalFeatures: [String]).

Query: dealers

No input. Returns: [{ id, email, status, firstName, lastName, phone, jobTitle, department, isWorking, isAutoAssignmentEnabled, locations }].

All mutations share the same CustomerIdentifierInput. In addition to identity/address fields, it accepts location (validated against dealership locations; appended to customer.locations; unknown values silently ignored) and leadSource (applied only on NEW customers; never overwritten on existing). All mutations also accept an optional top-level label: String that tags the customer with a dealership label (matched case-insensitively; created if missing; de-duplicated for the customer).

Mutation: submitLead

Input: customer (at least one of id, email, phone; optional firstName, lastName, address fields, comments), optional deal (vin: String!, paymentType: DealPaymentType (cash|leasing|financing), notes), optional label: String. Deduplicated by (customer, VIN): no duplicate deal is created if the customer already has one for the same VIN.

Mutation: submitAppointment

Input: customer identifier, appointment (type: AppointmentType! (sales|service|delivery|other), status: AppointmentStatus! (set|confirmed|declined|completed|missed|unresolved), optional appointmentAt: AWSDateTime, vin, location, notes), optional label: String.

Mutation: submitCreditApplication

Input: customer identifier, creditapplication (type: String "primary"|"co-signer" (not enum due to hyphen), applicantType: ApplicantType, personal info: firstName/lastName/ssn/birthDate, current address + homeOwnership: HomeOwnership/homeMonthlyPayment, employmentStatus: EmploymentStatus/employer/jobTitle/monthlyIncome, other income), optional label: String.

Mutation: submitTradein

Input: customer identifier, tradein (vehicle: vin/year/make/model/trim/mileage, condition: TradeinCondition (excellent|good|fair), valuation: minTrade/averageTrade/maxTrade/allowanceAmount/actualCashValue, payoff: payoffType: TradeinPayoffType (neither|loan|lease)/payoffLender/payoffAmount/equity), optional label: String.

VIN deduplication: if a VIN is provided and the customer already has a trade-in with that VIN, no new record is created. Trade-ins without a VIN are always inserted.

Mutation: submitReference

Input: customer identifier, reference (firstName, lastName, phone, email, relationship, address fields), optional label: String.

Mutation: submitLog

Input: customer identifier, log (type: SubmitLogType! (meeting|text|email|phone|note), body: String!, optional subject, datetime: AWSDateTime, transcription, summary), optional label: String.

Behavior: note creates an internal note (ActionType.Internal). Other types create communication logs (ActionType.Log) with data.communicationType = type. transcription and summary are appended to body automatically. All entries are attributed to the dealership's crm@space.auto system dealer.

When to use which mutation

  • submitLead — first contact / inbound interest; optionally attach a deal.
  • submitAppointment — scheduled future interactions (sales, service, delivery).
  • submitLog (type = phone/email/text/meeting) — record a past communication that already happened.
  • submitLog (type = note) — internal observation or context, not a customer-facing interaction.
  • submitCreditApplication / submitTradein / submitReference — structured records tied to deal closing.

Service 2: Real-time Events

Endpoint: wss://gql-events.space-auto.com/event/realtime
SubProtocol: aws-appsync-event-ws
Channel: /space/{dealershipId}/events

Connection

Auth is passed as a WebSocket subprotocol. Base64URL-encode { "host": "gql-events.space-auto.com", "Authorization": "{api_key}" } (replace + with -, / with _, strip =), prefix with header-, and pass as a subprotocol alongside aws-appsync-event-ws.

Protocol Messages

After connection: send { type: "connection_init" }. Receive connection_ack with connectionTimeoutMs. Subscribe: send { type: "subscribe", id: "uuid", channel: "/space/{dealershipId}/events", authorization: { host: "...", Authorization: "..." } }. Receive subscribe_success. Data arrives as { type: "data", id: "...", event: "JSON_STRING" }. Server sends { type: "ka" } keepalive periodically; close connection if not received within timeout. Unsubscribe: send { type: "unsubscribe", id: "..." }.

Event Shape

Events have: type (customer|deal|email|sms|call|internal|tradeins|payments|referents|external-lead|documents|appointments), action (create|update|merge|delete|send), dealershipId, actorType (customer|dealer), actorId.

Service 3: MCP Server

Endpoint: POST https://gql.space-auto.com/mcp
Protocol: JSON-RPC 2.0
Auth: Authorization: Bearer {api_key} (note the Bearer prefix)

Session Flow

1. Call initialize with { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name, version } }. 2. Call tools/list to discover tools. 3. Call tools/call with { name, arguments }. 4. Optionally call resources/list and resources/read with { uri }.

Tools (Read)

search_customers: args — search, skip, limit, sortField, sortOrder, unseenOnly, minDate, maxDate, includeIds, excludeIds.
search_vehicles: args — condition, make, model, trim, body, fuel, transmission, driveline, color, yearFrom/yearTo, priceFrom/priceTo, mileageFrom/mileageTo, status, limit, skip, sortField, sortOrder.
fetch_dealers: no args.

Tools (Write)

All write tools accept an optional label arg that tags the customer with a dealership label (case-insensitive match, auto-created if missing).

submit_lead: args — customerId/customerEmail/customerPhone (at least one), firstName, lastName, vin, paymentType (cash|leasing|financing), notes, label.
submit_appointment: args — customer identifier + type (sales|service|delivery|other), status (set|confirmed|declined|completed|missed|unresolved), appointmentAt, vin, location, notes, label.
submit_credit_application: args — customer identifier + credit application fields (type (primary|co-signer), applicantType, homeOwnership, employmentStatus, ssn, birthDate, address, employment, income), label.
submit_tradein: args — customer identifier + vehicle details, condition (excellent|good|fair) + valuation + payoffType (neither|loan|lease) + payoff info, label.
submit_reference: args — customer identifier + reference contact details, label.
submit_log: args — customer identifier + type (meeting|text|email|phone|note) [required], body [required], subject, datetime, transcription, summary, label. Use meeting/text/email/phone for past communications; use note for internal observations. Transcription/summary are appended to body server-side.

Resources

schema://graphql: Full GraphQL SDL schema. docs://api-guide: API usage guide (Markdown).

Decision Guide

TaskBest ServiceReason
Build a search UI with specific fieldsGraphQL APIField selection reduces payload
Live dashboard showing updatesEvents WebSocketPush-based, no polling
AI assistant answering questionsMCP ServerTool-calling protocol, structured for LLMs
Bulk data exportGraphQL APIPagination with skip/limit
Create leads from chatbotMCP ServerNatural fit for AI-driven workflows
Trigger alerts on new eventsEvents WebSocketReal-time notifications