Home/Help Center/REST API quickstart

Integrations

REST API Quickstart

Authenticate with an API key, then create a contact, send a template, read conversations and messages, and create a broadcast, with real curl requests for every call.

By Chirag Darji · Updated 1 Sept 2026 · 9 min read

On this page
  1. Before you start
  2. Steps
  3. What you will see
  4. Settings and options
  5. Troubleshooting
  6. Where does this fit next to webhooks and Zapier?
API keys in VGraple CRM with the REST API v1 endpoint reference listed below the keys

The REST API v1 is the same interface the Zapier app is built on, eleven endpoints under https://crm.vgraple.co.in/api/v1, authenticated with a Bearer API key, that any language capable of an HTTP request can call directly. This quickstart takes you from an empty terminal to a created contact, a sent template, a read of your conversation history and a first broadcast, with the exact request and response shapes.

REST API quickstart: first 5 of 8 steps

  1. 1Test authentication
  2. 2Create or update a contact
  3. 3List your approved templates
  4. 4Send an approved template
  5. 5List recent messages
The steps on this page, in order.

Before you start

  • You need an API key. See creating and rotating API keys; only the organisation owner or a super admin can generate one, from Settings > API Keys. If your integration only reads, create it as a Read-only key: every example in this guide except the sends and creates works with one.
  • You need at least one approved WhatsApp template if you plan to send a message, since the API only sends templates, never free-form text.
  • curl is used in every example here because it needs no setup, but the same requests work from any HTTP client, Postman, a Node fetch call, Python's requests, or a Zapier-adjacent tool like Make's HTTP module.

Steps

  1. Test authentication. Confirm your key works and see which organisation it belongs to.

Outbound webhooks in VGraple CRM with the events each endpoint subscribes to and its delivery count

curl https://crm.vgraple.co.in/api/v1/me \
  -H "Authorization: Bearer vgk_your_key_here"

A working key returns your organisation's name and key metadata; a wrong or missing key returns 401 with an error message. This exact call is also what Zapier uses as its connection test.

  1. Create or update a contact. POST /api/v1/contacts upserts by phone number, so calling it twice with the same number never creates a duplicate.
curl -X POST https://crm.vgraple.co.in/api/v1/contacts \
  -H "Authorization: Bearer vgk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+919876543210", "name": "Priya Sharma", "email": "[email protected]"}'

A first call returns 201 with {"contact": {...}, "created": true}. Calling it again with the same phone number and a different name returns 200 with {"contact": {...}, "created": false}, updating the name on the existing record.

  1. List your approved templates. You need an exact template name to send one, so fetch the list first if you don't already have it.
curl "https://crm.vgraple.co.in/api/v1/templates" \
  -H "Authorization: Bearer vgk_your_key_here"
{
  "templates": [
    { "name": "order_confirmation", "language": "en", "category": "UTILITY", "variable_count": 2 }
  ]
}
  1. Send an approved template. Only template sends are accepted; there is no way to send free-form text through the API.
curl -X POST https://crm.vgraple.co.in/api/v1/messages \
  -H "Authorization: Bearer vgk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"to": "+919876543210", "template": "order_confirmation", "variables": ["Priya", "#4521"]}'
{
  "message_id": "msg_01H8X...",
  "contact_id": "con_01H8W...",
  "conversation_id": "conv_01H8V..."
}

The variables array fills {{1}}, {{2}}, and so on in the template body, in order. This goes through the same tracked send path automation rules and owner alerts use, so it lands in the inbox and respects opt-out status.

  1. List recent messages. Useful for polling if you are not using webhooks for real-time delivery, and it is also the export endpoint: it returns every message in your inbox on every channel, imported WhatsApp history included.
curl "https://crm.vgraple.co.in/api/v1/messages?direction=inbound&limit=20" \
  -H "Authorization: Bearer vgk_your_key_here"
{
  "messages": [
    {
      "id": "msg_01H8X...",
      "wa_message_id": "wamid.HBgM...",
      "body": "Do you deliver to Rotterdam?",
      "type": "text",
      "direction": "inbound",
      "status": "delivered",
      "template_name": null,
      "error": null,
      "is_forwarded": false,
      "media": null,
      "reply_to": null,
      "wa_timestamp": "2026-07-13T10:00:00.000Z",
      "created_at": "2026-07-13T10:00:01.482Z",
      "updated_at": "2026-07-13T10:00:01.482Z",
      "channel": "whatsapp",
      "contact": { "id": "con_01H8W...", "name": "Asha Patel", "phone": "919876543210", "wa_id": "919876543210" },
      "conversation_id": "conv_01H8V..."
    }
  ],
  "next_cursor": "msg_01H8X..."
}

Paginate with cursor: pass the previous response next_cursor value as ?cursor=... to fetch the next page; next_cursor is null on the last page. Narrow the set with direction, type, status, conversation_id, contact_id, and a since/until window; flip to oldest-first with order=asc.

A message with an attachment carries a media object instead of null, whose url is an authenticated download link:

curl -L "https://crm.vgraple.co.in/api/v1/media/msg_01H8X..." \
  -H "Authorization: Bearer vgk_your_key_here" -o attachment.webp
  1. List conversations. The thread each message belongs to, with its contact, channel and lifecycle timestamps.
curl "https://crm.vgraple.co.in/api/v1/conversations?status=open&limit=20" \
  -H "Authorization: Bearer vgk_your_key_here"

Each row has id, channel, status, priority, label, unread_count, last_message_at, first_response_at, waiting_since, closed_at, csat_score and the contact. To read one thread, pass its id back as GET /api/v1/messages?conversation_id=<id>&order=asc. Pulling everything into your own database is covered in exporting your message history.

  1. Create a lead. POST /api/v1/leads accepts a name, phone, email, an optional pipeline_id (defaults to your default pipeline) and notes.
curl -X POST https://crm.vgraple.co.in/api/v1/leads \
  -H "Authorization: Bearer vgk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"name": "Asha Patel", "phone": "+919876543210", "notes": "Asked about the premium package"}'
  1. Create a broadcast, carefully. POST /api/v1/broadcasts accepts an audience mode, a template, variable mappings and an optional send_at. Omit send_at, or set it in the future, while testing; setting it to "now" sends immediately to the real audience through the exact same guards (opt-in, daily limit, quiet hours) as the composer.

What you will see

Every successful response is JSON with a resource key matching what you created or fetched (contact, lead, message_id, templates, leads). List endpoints wrap results in a plural key alongside next_cursor for pagination. Errors are a JSON object with an error string describing what went wrong, at a matching HTTP status code.

Settings and options

Setting or fieldWhat it doesDefault
Base URLhttps://crm.vgraple.co.in/api/v1Fixed
Auth headerAuthorization: Bearer vgk_...Required on every request
Rate limitRequests allowed per key per hour600, rolling window
Broadcast creation limitExtra cap on POST /api/v1/broadcasts20 campaigns/hour per organisation
Paginationlimit (max 100) and cursor on list endpointslimit=50
Orderingorder=asc or order=desc on list endpoints, by creation timedesc (newest first)
Date windowsince and until, ISO 8601, both inclusive, on creation timeUnbounded
Key access levelFull access, or read-only (403 on every write)Full access

Troubleshooting

SymptomLikely causeFix
Every request returns 401Missing, malformed, rotated or revoked API keyRecheck the Authorization: Bearer header and confirm the key still exists in Settings > API Keys
POST /api/v1/messages returns "No approved template named..."The template name is misspelled, not yet approved, or disabledCall GET /api/v1/templates first and use the exact name field it returns
A create call returns 400 with a field-specific messageThe request body failed validation (for example an invalid phone number or a missing required field)Read the error message directly; it names the offending field
429 "Rate limit exceeded"More than 600 requests in the last rolling hour on this keySlow down or batch calls; give a second integration its own key rather than sharing one
403 "This API key is read-only"The key was created with read-only access and the call writesUse a full-access key for writes; a key access level cannot be changed after creation
A list call returns 400 naming since, order or limitA parameter was malformed, for example a non-ISO date or an inverted since/until windowRead the error message; it names the parameter and the accepted form
POST /api/v1/broadcasts sent to the wrong audience unexpectedlysend_at was set to "now" or a past-due time against a broader audience than intendedAlways test with a small tagged audience first; review the audience mode and template mapping before setting a live send time
A phone number in a response looks different from what you sentNumbers are normalised to E.164 on saveExpected; store and compare using the normalised value the API returns, not your original input string

Where does this fit next to webhooks and Zapier?

The REST API is the foundation both the Zapier app and outbound webhooks are built on. If you want VGraple CRM to notify your system the moment something happens, use webhooks instead of polling this API in a loop; if you need to push data in, like creating a contact or sending a template from your own system, this API (or Zapier as a no-code layer over it) is the right tool. GET/POST /api/v1/hooks and DELETE /api/v1/hooks/{id} let you manage REST hook subscriptions directly with your API key, the same mechanism Zapier uses when a Zap turns on, including subscribing to any of the platform's full event catalog, even the few not yet exposed as checkboxes in Settings > Webhooks.

Frequently asked questions

What is the base URL for the API?
https://crm.vgraple.co.in/api/v1, followed by the endpoint, for example https://crm.vgraple.co.in/api/v1/contacts. Every request needs an Authorization: Bearer vgk_... header with a key from Settings > API Keys.
Which endpoints are available?
Eleven: GET /api/v1/me (auth test), GET and POST /api/v1/contacts, GET and POST /api/v1/messages, GET /api/v1/conversations, GET /api/v1/media/{message_id}, GET and POST /api/v1/leads, GET /api/v1/templates, GET /api/v1/pipelines, GET and POST /api/v1/hooks plus DELETE /api/v1/hooks/{id}, and GET and POST /api/v1/broadcasts. The same list, with the parameters each one takes, is shown under Settings > API Keys in the app.
Can I read my full WhatsApp message history through the API?
Yes. GET /api/v1/messages returns every message in your inbox across all channels, including the one-time WhatsApp history import that coexistence brings in, with the original WhatsApp timestamps. Walk it with ?order=asc and the cursor to export the lot. See exporting your message history for the full recipe.
How do I download an image or document a customer sent?
Every message with an attachment returns a media object whose url points at GET /api/v1/media/{message_id}. Call it with the same Bearer key and it streams the file back. The raw storage URL is never returned, because it would be publicly readable and would outlive the key that fetched it.
Can I send a free-form message through the API?
No, only an approved template. POST /api/v1/messages accepts a template name, language and body variables; Meta requires a template for any business-initiated message outside a conversation's 24-hour window, and an API caller has no reliable way to know whether that window is open for a given contact.
How do I paginate through a large list, like messages or contacts?
Pass a limit (up to 100, default 50) and, after the first page, the next_cursor value the previous response returned, as the cursor parameter. The response next_cursor is null once you have reached the last page. Every list endpoint also accepts order=asc or desc and a since/until window on creation time, both inclusive.
What HTTP status codes should I expect?
201 for a successful create, 200 for a successful read or an upsert that matched an existing record instead of creating one, 400 for invalid input, 401 for a missing or wrong API key, 404 for a resource that does not exist in your organisation, and 429 if you exceed the rate limit.
Does creating a broadcast through the API actually send it?
Yes, if you include a send time of now or a past-due schedule, going through the exact same pacing, opt-in and daily-limit checks as the broadcast composer in the UI. Test with a small tagged audience before wiring any automated process to call this endpoint.
Do I need a different API key for each endpoint?
No, one key authenticates every endpoint your organisation has access to. There is no per-endpoint scoping, but there are two access levels. A full-access key can read and write, while a read-only key is accepted on every GET and rejected with a 403 on anything that writes. Give any integration that only pulls data out a read-only key.

Run your WhatsApp on VGraple CRM

Free forever plan, official Meta WhatsApp Business API, set up in 15 minutes. No card needed.