Heroshhi API reference
The same REST API the Heroshhi app uses: leads, messages, WhatsApp, drips and broadcasts. Log in to get a token, then send it with each request.
https://api.heroshhi.com/apiAuthentication
Log in once to get a token, then include it in the header of every request. It proves who you are, like a session key.
Every request: Authorization: Bearer <token>
// Log in to receive a token
POST https://api.heroshhi.com/api/auth/login
Content-Type: application/json
{
"email": "admin@yourcompany.com",
"password": "your_password"
}
// Reply
{
"success": true,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Rate limits
To keep things fair, there's a limit on requests in a short time. Go over it, and you get 429 Too Many Requests: wait a moment and try again.
| Kind of endpoint | Requests | Per |
|---|---|---|
| Standard API | 100 | 15 minutes |
| Webhooks | 500 | 15 minutes |
| Logging in | 5 | 15 minutes |
Auth
/api/auth
| Method | Endpoint | What it does |
|---|---|---|
| POST | /login | Log in with email and password |
| POST | /signup | Create an account |
| POST | /forgot-password | Ask for a password reset |
| POST | /reset-password | Set a new password with the reset token |
| POST | /verify-2fa | Check a two-step code |
| GET | /me | The logged-in user |
Leads
/api/leads
| Method | Endpoint | What it does |
|---|---|---|
| GET | / | List leads, with filters (including stage) and pages |
| GET | /:id | One lead, in full |
| POST | / | Create a lead |
| PUT | /:id | Update a lead |
| DEL | /:id | Delete a lead |
| POST | /import | Import leads in bulk (mode: fresh or history) |
| POST | /bulk-update | Update several leads |
| POST | /bulk-assign | Assign several leads |
| POST | /bulk-delete | Delete several leads |
| GET | /analytics | Lead numbers for reports |
| POST | /:id/engagement | Record an engagement |
Messages
/api/messages
| Method | Endpoint | What it does |
|---|---|---|
| GET | / | List messages, with filters |
| GET | /:leadId | Messages with one lead |
| POST | /send | Send a message to a lead |
| GET | /templates | Your WhatsApp templates |
/api/whatsapp
| Method | Endpoint | What it does |
|---|---|---|
| GET | /status | WhatsApp connection status |
| POST | /connect | Connect your WhatsApp Business API |
| DEL | /disconnect | Disconnect WhatsApp |
| POST | /send | Send a message (refused if the contact opted out) |
| POST | /send-bulk | Send in bulk (opted-out contacts left out) |
| GET | /coexistence | Coexistence settings |
| PUT | /coexistence | Change coexistence settings |
Drips and broadcasts
| Method | Endpoint | What it does |
|---|---|---|
| GET | /api/drips | List drip sequences |
| POST | /api/drips/:id/enroll | Add leads to a sequence |
| POST | /api/broadcasts | Create and send a broadcast |
| GET | /api/broadcasts/:id | One broadcast, with results |
Lead and message
Lead
| Field | Type | Meaning |
|---|---|---|
| name | String, required | The lead's full name |
| phone | String, required | Phone number with country code |
| stage | Enum | new / contacted / qualified / converted / not_qualified / lost. Set by Heroshhi from what happened; read it, don't write it |
| stageAt | Date | When the lead reached its stage |
| status | Enum | pending / contacted / qualified / converted / lost (older field, kept for compatibility) |
| segment | Enum | hot / warm / cold: the team's own judgement |
| assignedTo | ObjectId | The team member who owns the lead |
| isOptedOut | Boolean | Whether the contact has opted out of messages |
Message
| Field | Type | Meaning |
|---|---|---|
| leadId | ObjectId | The lead |
| direction | Enum | inbound / outbound |
| status | Enum | queued / sent / delivered / read / failed |
Lead webhook
Send leads into Heroshhi from any other system, such as your website or a form builder. No login needed: post the details, and the lead appears in your account.
POST /api/webhook/lead
{
"name": "John Doe",
"phone": "919876543210",
"email": "john.doe@example.com",
"source": "Website form",
"inquiry": "Product enquiry",
"userId": "your_account_id"
}
Meta Conversions API
Meta knows a person clicked your ad. It doesn't know whether they became a customer. Heroshhi sends that back, so Meta can aim your ads at people who actually convert, not just people who fill in forms.
Three kinds of lead, three ways of sending
Meta identifies each source differently. With the wrong identifier, an event is accepted but never matched to an ad, so Heroshhi detects the source and sends it the right way.
| Lead came from | Identifier | action_source | Sent to |
|---|---|---|---|
| Website form | fbclid | website | Pixel |
| Click-to-WhatsApp ad | ctwa_clid | business_messaging | Pixel |
| Meta Instant Form | lead_id | system_generated | CRM dataset |
Click-to-WhatsApp identifiers come in the referral object of the customer's first message, and are picked up automatically. Instant Form leads carry a 15 to 17 digit lead ID in the id column of Meta's export.
Events Heroshhi sends
| Event | Sent when |
|---|---|
Lead | A new lead arrives, from any source |
Qualified | The lead shows real interest: a call logged as interested or a callback, or an appointment booked |
Converted | The lead becomes a customer, with the value when a payment is recorded |
Not Qualified | Never a real prospect: wrong number, spam, or a clear no before any interest |
Lost | A real prospect who didn't buy, for example on price or because they booked elsewhere |
Each event's name can be changed in your settings, to match the stages you set up in Meta's lead funnel.
Settings
PUT /api/users/conversion-api
{
"facebook": {
"enabled": true,
"pixelId": "123456789012345",
"accessToken": "EAAxxxxx...",
"crmDatasetId": "987654321098765",
"testEventCode": "TEST12345"
},
"events": {
"sendLeadEvent": true,
"sendQualifiedEvent": true,
"sendConversionEvent": true,
"sendDroppedEvent": true // covers both Not Qualified and Lost
}
}
crmDatasetId is only needed for Meta Instant Forms. Meta keeps website events and CRM events in separate datasets, so lead stages need their own: create one in Events Manager under Connect data sources, then CRM. Leave it empty, and everything goes to the Pixel.
Worth knowing before you switch it on
- Matching starts the day you switch it on. Older leads carry no click ID and can't be matched afterwards.
- Meta needs a few hundred results before delivery changes. Expect three to four weeks, not three days.
- Nothing useful reaches Meta unless your team records outcomes. If every lead stays New, Meta learns nothing.
- Personal details are hashed with SHA-256 before sending. Meta can match them but can't read them.
- Remove the test event code once delivery is confirmed, or real events keep landing in the test view.
Checking delivery
Copy the test event code from Events Manager into your settings, change a lead's stage, and watch the Test events tab. The event should appear within a minute or two. Instant Form events appear under the CRM dataset, not the Pixel.
Opt-outs
Every sending endpoint checks whether the contact has opted out before sending, so you stay within Meta's rules without checking yourself. A single message to an opted-out contact is refused:
// 400 Bad Request
{
"success": false,
"message": "Cannot send message - contact has opted out",
"error": "OPT_OUT_BLOCKED"
}
Errors
Errors always come back in the same shape, so your code can handle them one way:
{
"success": false,
"message": "What went wrong",
"error": "More detail (development only)"
}
| Code | Meaning |
|---|---|
200 | It worked |
201 | Created |
400 | Something in the request is wrong |
401 | The token is missing or has expired |
404 | That record doesn't exist |
429 | Too many requests: wait and try again |
Security
Headers
Every reply carries security headers against common attacks such as cross-site scripting and clickjacking: Content-Security-Policy, X-Content-Type-Options, X-Frame-Options: DENY and X-XSS-Protection.
Input checks
Everything sent to the API is checked and cleaned, against code injection, harmful HTML and similar attacks.
Who can call it
The API only accepts requests from the Heroshhi app. Webhook endpoints are the exception: they're open, so Meta and other services can deliver events.
