Pongg
  • Product overviewEverything Pongg does, in one placeAI ReceptionistAnswers when your team can'tAppYour front desk in your pocketCall Lead TrackingSee which marketing makes the phone ringOutbound Voice AgentCalls your customers for youWin-back CampaignsBring dead leads back to lifeEmail AutomationsEmails that send and answer themselvesWebchat BotBooks jobs from your websiteIntegrationsWorks with your CRM and calendarTexting HubText customers from app and web
  • RoofingStorm restoration and repairsCleaning servicesResidential and commercial cleaningPlumbingLeaks, drains and emergenciesHVACHeating and coolingOther tradesCleaning, electrical, pest and more
  • Pricing
  • About Us
  • Blog
Log in Request Demo
Home
ProductProduct overviewAI ReceptionistAppCall Lead TrackingOutbound Voice AgentWin-back CampaignsEmail AutomationsWebchat BotIntegrationsTexting Hub
IndustriesRoofingCleaning servicesPlumbingHVACOther trades
PricingAbout UsCareersBlog
Log inRequest Demo

Developer guide

  1. Quick start
  2. Data Pongg sends
  3. Authentication
  4. REST operations
  5. MCP transport
  6. Receipts, time and retries
  7. Security and limits
  8. Launch checklist

Build with Pongg

Custom CRM bridge

Keep your CRM. Let Pongg send caller details, capture leads and request or confirm appointments through a bridge your developers control.

Contract pongg.crm.v1 · Updated October 19, 2026

Download OpenAPI 3.1 JSON · Open Integrations in Pongg

Quick start

Your developer builds a small, customer-owned HTTPS service that translates this contract into your CRM's approved API. Enter that service's base URL in Pongg → Integrations → Custom CRM. A vendor's generic API URL will not work by itself.

  1. Implement contacts.upsert and at least one of leads.create or appointments.create.
  2. Implement the read-only capabilities response below. Add search and real availability only when your bridge supports them.
  3. Choose REST or MCP, then an API key or OAuth service account in Pongg. Bind the credential to the client tenant.
  4. Connect to run the capabilities check. With your Success Team, send a clearly labelled test lead and test a booking before enabling live delivery.

The connection check reads capabilities only; it does not create a contact, lead or appointment. Demo and sandbox accounts cannot connect this bridge to a real CRM. This guide describes a connector to your service, not a public Pongg API, OAuth app marketplace or general-purpose MCP agent.

Data Pongg sends

These tables describe the actual serialized fields. A field marked Present is included even when its string is empty or its array has no items. Optional fields are omitted, not sent as null. Booking start and end are always non-null offset-aware timestamps. Accept additive fields so compatible updates do not break your bridge.

There are no dedicated fields for full transcripts, recording URLs, payment or card data, or arbitrary metadata in this bridge contract. Summary and notes are free-form call information, so handle them according to your own data controls.

Contact

FieldTypePresenceMeaning
full_namestringPresentCaller name as captured; empty when unknown.
first_namestringPresentCaller given name when available; may be empty.
last_namestringPresentCaller family name when available; may be empty.
addressstringPresentService or street address captured from the caller; may be empty.
sourcestringPresentLead attribution text, for example Pongg (phone); may be empty.
call_idstringPresentOriginating call identifier. May be empty during an in-call action before post-call processing.
phonesarray<string>PresentArray of callback phone strings. Normalized where possible; can be empty or contain ambiguous bare-digit identities.
emailsarray<string>PresentArray of email strings captured from the caller. Can be empty; no assumed lowercase or strict format.
tagsarray<string>PresentArray of source tag strings, for example Pongg. Can be empty.
postal_codestringOptional, omitted when absentOptional captured postal code. Omitted when no postal code is supplied.

Phones are normalized where possible. Ambiguous numbers can remain bare digits. Emails are sent as captured; do not assume a strict email format or lowercase value. Contact upsert needs at least one usable phone or email and must match an existing identity before creating a duplicate.

Lead

FieldTypePresenceMeaning
job_typestringPresentRequested service or job type captured from the call; free-form string, not a fixed enum. May be empty.
summarystringPresentShort description of the caller need or call result; may be empty.
notesstringPresentAdditional free-form call details and instructions; may be empty.
urgencystringPresentCaptured urgency text or label. No fixed enum is required; may be empty.
sourcestringPresentLead attribution text, for example Pongg (phone); may be empty.
call_idstringPresentOriginating call identifier for provenance and call-based idempotency; may be empty.
contactContactPresentNested caller Contact object with the fields listed above.
locationLocationOptional, omitted when absentOptional configured business location: id and name. Omitted when the call has no location.

Appointment

FieldTypePresenceMeaning
contact_external_idstringPresentDurable contact ID previously returned by this bridge.
timezonestringPresentValid IANA timezone, for example America/Chicago.
startstringPresentOffset-aware ISO 8601 instant. Required and non-null for booking.
endstringPresentOffset-aware ISO 8601 instant. Required and non-null for booking.
slot_textstringPresentVerbatim or display time description from the call. It is not parsed as a substitute for start and end; may be empty.
job_typestringPresentRequested service or job type as captured; free-form string, not a fixed enum. May be empty.
addressstringPresentService address for the visit as captured; may be empty.
notesstringPresentFree-form booking details or instructions; may be empty.
titlestringPresentAppointment title or display label; may be empty.
sourcestringPresentAppointment attribution text, for example Pongg (phone); may be empty.
locationLocationOptional, omitted when absentOptional configured business location: id and name. Omitted when the call has no location.
service_idstringOptional, omitted when absentOptional service identifier supplied with the booking. Omitted when empty.

The appointment object does not include call_id, status or caller_confirmed. A location, when present, contains only id and name. The contact itself has no location object.

Location

FieldTypePresenceMeaning
idstringPresentConfigured location ID.
namestringPresentConfigured location name; may be empty.

Authentication

API key: Pongg sends the configured key as a Bearer credential on every bridge request. Keys must contain visible ASCII characters without spaces or control characters. Store credentials in the Custom CRM connection form, never in the endpoint URL.

Authorization: Bearer YOUR_BRIDGE_API_KEY

OAuth 2.0 client credentials: provide your HTTPS token URL, client ID, client secret and optional scope. The scope field is sent only when provided. Pongg sends a form POST using HTTP Basic client authentication. Client ID and secret are form-encoded before Basic encoding. This is a machine account flow, not an end-user browser consent screen.

POST https://auth.example.com/oauth/token
Authorization: Basic BASE64_ENCODED_CLIENT_CREDENTIALS
Content-Type: application/x-www-form-urlencoded
Accept: application/json

grant_type=client_credentials&scope=crm.read+crm.write
{
  "access_token": "EXAMPLE_ACCESS_TOKEN",
  "token_type": "Bearer",
  "expires_in": 3600
}

Return token_type: "Bearer". expires_in is optional; only a numeric lifetime greater than 30 seconds is cached, for at most five minutes with a 30-second safety margin. Without a usable lifetime, Pongg requests a fresh grant next time. The cache is bound to tenant, account, endpoint, configuration and credentials. A bridge HTTP 401 causes one fresh grant and one replay of the same request; another 401 stops. API-key 401s are not replayed through OAuth.

REST operations

Every operation is an HTTPS POST. Pongg appends the path to your configured base URL. For a base of https://crm.example.com/pongg/v1, capabilities is https://crm.example.com/pongg/v1/capabilities. All examples below are synthetic.

Every body contains tenant_id and contract_version. All three write operations also carry the same opaque 64-character hexadecimal key in the Idempotency-Key header and idempotency_key body field. Reads do not carry a write key.

POST /capabilities

Return the exact contract version and only the supported operation names. Include contacts.upsert and at least one of leads.create or appointments.create. If you advertise availability.list, also implement appointments.create and return booking_closes_availability:true for the same calendar.

Request JSON
{
  "tenant_id": "tenant-example-001",
  "contract_version": "pongg.crm.v1"
}
Response JSON
{
  "contract_version": "pongg.crm.v1",
  "operations": [
    "contacts.search",
    "contacts.upsert",
    "leads.create",
    "availability.list",
    "appointments.create"
  ],
  "booking_closes_availability": true
}

POST /contacts/search

Both search fields are strings and may be empty. Return contact:null when there is no match, or a nonblank durable contact ID. Do not invent a match.

Request JSON
{
  "tenant_id": "tenant-example-001",
  "contract_version": "pongg.crm.v1",
  "phone": "+12145550148",
  "email": "jordan@example.com"
}
Response JSON
{
  "contact": {
    "id": "contact-example-001"
  }
}
{
  "contact": null
}

POST /contacts/upsert

Match phone or email identity before creating a contact. Return a durable nonblank ID and a real boolean is_new. String values such as "false" do not satisfy the response.

Request JSON
{
  "tenant_id": "tenant-example-001",
  "contract_version": "pongg.crm.v1",
  "contact": {
    "full_name": "Jordan Example",
    "first_name": "Jordan",
    "last_name": "Example",
    "phones": [
      "+12145550148"
    ],
    "emails": [
      "jordan@example.com"
    ],
    "address": "100 Example St, Dallas TX 75201",
    "source": "Pongg (phone)",
    "tags": [
      "Pongg"
    ],
    "call_id": "example-call-001",
    "postal_code": "75201"
  },
  "idempotency_key": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
Response JSON
{
  "id": "contact-example-001",
  "is_new": false
}

POST /availability

The window includes the requested interval, IANA zone, weekly windows, duration and job type. Weekly windows are [day, openingMinute, closingMinute], with 0=Monday; an empty array means not supplied. slot_minutes can be 0 when unspecified. Return at most 50 real openings inside the interval, with end after start. Pongg sorts and deduplicates slots by instant. Service IDs or arrival-window metadata in a slot response are not consumed by this bridge.

Request JSON
{
  "tenant_id": "tenant-example-001",
  "contract_version": "pongg.crm.v1",
  "window": {
    "start": "2026-10-20T00:00:00+00:00",
    "end": "2026-10-21T00:00:00+00:00",
    "timezone": "America/Chicago",
    "windows": [
      [
        1,
        420,
        1080
      ]
    ],
    "slot_minutes": 60,
    "job_type": "Roof repair"
  }
}
Response JSON
{
  "slots": [
    {
      "start": "2026-10-20T15:00:00+00:00",
      "end": "2026-10-20T16:00:00+00:00",
      "timezone": "America/Chicago"
    }
  ]
}

POST /appointments

Check capacity and create the appointment atomically. Return a durable ID, confirmed:true, exact requested start/end instants and a valid IANA timezone. An unconfirmed request, shifted time, empty ID or missing timestamp is a failed booking. Return HTTP 409 if the slot is taken.

Request JSON
{
  "tenant_id": "tenant-example-001",
  "contract_version": "pongg.crm.v1",
  "appointment": {
    "contact_external_id": "contact-example-001",
    "timezone": "America/Chicago",
    "start": "2026-10-20T15:00:00+00:00",
    "end": "2026-10-20T16:00:00+00:00",
    "slot_text": "Tuesday at 10 AM Central",
    "job_type": "Roof repair",
    "address": "100 Example St, Dallas TX 75201",
    "notes": "Synthetic example: inspect a small roof leak.",
    "title": "Roof repair visit",
    "source": "Pongg (phone)",
    "location": {
      "id": "dallas",
      "name": "Dallas office"
    },
    "service_id": "repair-inspection"
  },
  "idempotency_key": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
Response JSON
{
  "id": "appointment-example-001",
  "confirmed": true,
  "start": "2026-10-20T15:00:00+00:00",
  "end": "2026-10-20T16:00:00+00:00",
  "timezone": "America/Chicago"
}

POST /leads

Create or return the existing lead for the same idempotency key. Return its durable nonblank ID. A generic ok:true acknowledgement is not a receipt.

Request JSON
{
  "tenant_id": "tenant-example-001",
  "contract_version": "pongg.crm.v1",
  "lead": {
    "contact": {
      "full_name": "Jordan Example",
      "first_name": "Jordan",
      "last_name": "Example",
      "phones": [
        "+12145550148"
      ],
      "emails": [
        "jordan@example.com"
      ],
      "address": "100 Example St, Dallas TX 75201",
      "source": "Pongg (phone)",
      "tags": [
        "Pongg"
      ],
      "call_id": "example-call-001",
      "postal_code": "75201"
    },
    "job_type": "Roof repair",
    "summary": "Caller requested an inspection.",
    "notes": "Synthetic example only.",
    "urgency": "routine",
    "source": "Pongg (phone)",
    "call_id": "example-call-001",
    "location": {
      "id": "dallas",
      "name": "Dallas office"
    }
  },
  "idempotency_key": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
Response JSON
{
  "id": "lead-example-001"
}

MCP transport

Choose MCP to use the fixed Pongg tools over Streamable HTTP 2026-07-28. Enter the MCP endpoint URL itself; no REST suffix is appended. Arguments and structured responses match the REST contract.

OperationFixed tool
capabilitiespongg_capabilities
contacts.searchpongg_contacts_search
contacts.upsertpongg_contacts_upsert
availability.listpongg_availability_list
appointments.createpongg_appointments_create
leads.createpongg_leads_create

Each POST carries the following headers. Write calls also include Idempotency-Key. The protocol, method and tool-name headers must match the JSON body.

Content-Type: application/json
Accept: application/json, text/event-stream
Authorization: Bearer YOUR_BRIDGE_CREDENTIAL
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: pongg_capabilities
{
  "jsonrpc": "2.0",
  "id": "0123456789abcdef01234567",
  "method": "tools/call",
  "params": {
    "name": "pongg_capabilities",
    "arguments": {
      "tenant_id": "tenant-example-001",
      "contract_version": "pongg.crm.v1"
    },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "Pongg CRM bridge",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": "0123456789abcdef01234567",
  "result": {
    "resultType": "complete",
    "isError": false,
    "structuredContent": {
      "contract_version": "pongg.crm.v1",
      "operations": [
        "contacts.search",
        "contacts.upsert",
        "leads.create",
        "availability.list",
        "appointments.create"
      ],
      "booking_closes_availability": true
    }
  }
}

Echo the 24-character JSON-RPC request ID; it correlates the response and is separate from the 64-character write idempotency key. Return resultType: "complete" and the matching REST response object in structuredContent. Return isError: false on success. Plain text is not parsed into a receipt. JSON and bounded request-scoped SSE data frames are supported; notifications without request IDs may precede the final response.

No initialize/session exchange, arbitrary tool discovery, resources, prompts, sampling, elicitation, browser sign-in or input-required conversations are supported. Server requests, JSON-RPC errors and incomplete results fail the operation. Remote prose and tool descriptions never become instructions for the agent.

Receipts, time and retries

Use explicit UTC offsets in every timestamp and an IANA timezone such as America/Chicago. Appointment input is checked before sending; a naive or nonpositive interval is refused. A receipt must match both requested instants exactly. A prior availability read does not reserve capacity.

Persist a unique idempotency-key-to-receipt mapping in the same CRM transaction as each write. Replaying a write must return the original record. Contact keys are based on normalized identity; appointment keys use tenant, contact, UTC interval, service or job type and location; lead keys use call ID and job type when available. Mutable descriptions do not change those appointment or call-based lead keys. Treat keys as opaque.

ResultWhat Pongg does
2xxParses the required JSON object or MCP structured receipt. An empty response or acknowledgement alone is not success.
409Treats the operation as a conflict or unavailable slot. No booking is confirmed and it is not automatically retried.
429 or 5xxPost-call delivery can retry under the worker policy with the same idempotency key. Retry-After is bounded to five minutes.
Network failureCan retry in post-call delivery. A call-time operation returns a structured failure or fallback rather than promising an unconfirmed booking.
401OAuth service accounts receive one fresh grant/replay. Further 401s, and API-key 401s, require corrected access.
Other 4xxRequires corrected configuration or data. Remote error bodies are not displayed verbatim or treated as successful receipts.

Only the capabilities probe is part of connecting. The bridge must actually honor identity matching, idempotency, permissions and atomic slot closure. Test those behaviors with your team before relying on live calls.

Security and limits

  • Use public HTTPS with a verifiable certificate. Every DNS answer must be public; the selected address is pinned while TLS verifies the original hostname.
  • Redirects, proxies, URL credentials, query strings and fragments are not supported. Use a clean endpoint path.
  • Each HTTP exchange has a 10-second total deadline. Request and response bodies are each limited to 1 MiB. Return uncompressed JSON or uncompressed MCP SSE.
  • Bind each credential to its tenant. Reject a tenant mismatch even if the incoming phone number or CRM record looks valid.
  • Store and rotate credentials securely. Pongg uses its tenant-bound encrypted integration store; never paste production secrets into examples, URLs or support messages.
  • Keep caller data limited to the workflow your company authorized. Free-form notes and summaries need the same access controls as CRM records.

Launch checklist

  1. Confirm your vendor permits the required API access and that your account has write permissions.
  2. Connect the customer-owned bridge and verify the read-only capabilities result.
  3. Send a labelled setup lead. Confirm the mapped fields and durable CRM ID with your Success Team.
  4. Repeat the same idempotency key and confirm it returns one record.
  5. Test empty availability, an occupied slot, a successful booking and the same booking replay. Verify the booked slot disappears from the same availability source.
  6. Test the client's local timezone, overnight intervals and daylight saving changes.
  7. Test rejected access, rate limits and a timeout after a successful write. Confirm retries reuse the original receipt.

Versioning: all requests declare pongg.crm.v1. Accept compatible additive fields. A breaking contract requires a separately coordinated version; do not silently change field meaning or return a different major contract.

Your Success Team can help confirm the intended lead, appointment and calendar behavior. Start in Pongg Integrations.

Pongg

Pongg picks up the calls your team can't get to, books the job, and shows you what every call was worth. Built for roofing, plumbing, HVAC and cleaning companies.

Product

  • Product
  • Pricing
  • Industries
  • Log in

Industries

  • Roofing
  • Cleaning services
  • Plumbing
  • HVAC

Company

  • About us
  • Careers
  • Blog
  • Request a demo
  • Terms of Service
  • Privacy Policy
  • Website Terms
© 2026 Pongg Inc. · pongg.com · Terms of Service · Privacy Policy · Website Terms · Stay Ahead.