Link to the conversation from your CRM
Store, on each customer record in your system, a link that opens their conversation in Meteor, matched by phone number or tax ID.
Your team lives in your CRM, but customer conversations happen in Meteor. Every contact comes with an app_url: the link that opens their conversation in Meteor. Store it on the customer record in your system and whoever is looking at that record reaches the chat in one click.
The link is stable: it doesn't include the workspace name, so it keeps working if the workspace is renamed. Opening it requires a Meteor session with access to that contact. It is not a public link to the conversation.
{
"id": 4821,
"data": { "$name": "Ada Lovelace", "$phone": "+573001112233", "nit": "900123456" },
"app_url": "https://met.meteor.com.co/abrir/137/contacto/4821"
}
There are two ways to fill it in. You can ask Meteor whenever you need it (step 2), or let Meteor notify you every time a contact is created, changes or writes in (step 3). If your system can receive a POST, the second keeps the record current without any polling.
Before you start
Your key needs contacts:read. To receive events, also webhooks:manage and conversations:read.
1. Pick the value you match on
Your system does the matching, with a value both sides have:
- Phone —
data.$phone, in international format with+(+573001112233). If your system stores it differently (3001112233,57 300 111 2233), normalize it before comparing: the filter compares the exact text. - Tax ID, national ID or another document — works if it exists as a custom contact field in Meteor and is filled in. Custom fields use their
field_keywithout$(nit,cedula). List the workspace's fields withGET /variables/contact-fields.
2. Ask for a customer's link
Find the contact with a filter and read its app_url:
import Met from '@meteor.ia/sdk';
const met = new Met({ apiKey: process.env.MET_API_KEY! });
async function meteorLink(taxId: string): Promise<string | null> {
const page = await met.contacts.list({
filters: JSON.stringify([{ field: 'nit', op: 'eq', value: taxId }]),
limit: 2,
});
if (page.data.length !== 1) return null; // 0: not in Meteor · 2+: check for duplicates
return page.data[0].app_url ?? null;
}
A malformed condition is ignored, it doesn't error: if you misspell the field name, the filter returns every contact. That's why the example asks for limit: 2 and only accepts exactly one result.
If you already have the contact's id, met.contacts.retrieve(id) also returns the app_url.
3. Let Meteor send it
Subscribe to contact events. Every event about a contact carries its app_url in data: contact.created, contact.updated, contact.message.received, contact.message.sent and conversation.handoff.
const sub = await met.webhooks.subscriptions.create({
url: 'https://your-crm.com/webhooks/met',
enabled_events: ['contact.created', 'contact.updated', 'contact.message.received'],
description: 'Conversation link on the customer record',
});
console.log(sub.secret); // whsec_… — shown ONCE
The endpoint verifies the signature, answers fast and stores the link on your record:
app.post('/webhooks/met', express.raw({ type: 'application/json' }), async (req, res) => {
let event;
try {
event = met.webhooks.constructEvent(
req.body,
req.headers['x-met-signature'] as string,
process.env.MET_WEBHOOK_SECRET!,
);
} catch {
return res.status(400).send('invalid signature');
}
res.sendStatus(200);
const { contact_id, app_url } = event.data as { contact_id: number; app_url?: string };
if (!app_url) return;
const contact = await met.contacts.retrieve(contact_id);
await yourCrm.saveLink({
phone: contact.data.$phone,
taxId: contact.data.nit,
link: app_url,
});
});
Saving the same link twice does no harm, so this endpoint survives retries without keeping track of which events it already processed. The recipe Receive signed events in your backend covers signatures and retries in detail.
No code: from a flow
If you don't have a backend, a Meteor flow can send the link to your system with an HTTP request node. The variable is $contact.app_url, just like $contact.phone or $contact.name.