mySDRDocs

API reference

Log activity

Tell mySDR what you did or heard outside it: an email you sent, a reply, a bounce, a meeting, not interested, an out-of-office, an unsubscribe, a referral, or a note. It can be about a person (lead), a company (company, e.g. when you mailed its general address) or just an address (email). Someone not in the Leads or Companies table yet is added. Log every email you send, with step, template and thread_id, so POST /outreach/check knows what went out. Log your research steps too, as a note on the company with source_url (the careers page you counted, the annual accounts you read). A note without lead, company or email is your work log: a run you finished, a source you tried, a decision the user made. The user sees every line on the Activity tab. Send one activity, or { "activities": [...] } with up to 100 in order; each is logged on its own. mySDR keeps your whole process, so the user never needs a spreadsheet next to it: the state of each company and person (status with a reason, next action, email and contact points, your own fields), every email, reply and bounce you log, the exclusions, and a check before every send. The user sees all of it in the Leads and Companies tabs.

POSThttps://mysdr.app/api/v1/outreach/activity
View Markdown

What an activity does besides the log line:

  • email_sent — with text, the email shows in the mySDR Inbox and is kept, so you can read it back (GET /outreach/activity?with_text=true); a new address becomes the person's email or a contact point of the company; a planned email (next_action.email) with the same step is cleared (logged without a step, one planned without a step).
  • email_replied — stops LinkedIn for that person in the whole organization; a planned follow-up is cleared; with lead_status the row gets it (later with until = recontact on that day).
  • email_bounced — the address gets the status bounced and goes on the exclusions; a planned follow-up is cleared.
  • meeting_booked — stops LinkedIn; lead status meeting.
  • not_interested — stops LinkedIn and blocks a new sequence; lead status not_interested; the next action is cleared.
  • out_of_office — away until until; a next action before that day moves to it.
  • unsubscribed — the address goes on the exclusions; stops LinkedIn; the next action is cleared.
  • referral — referral (the person they sent you to) lands in Leads at the same company and in the same campaign (referral_row in the answer), with a line in their log. It counts as the referrer's answer: their planned follow-up is cleared, and POST /outreach/check does not block the person they referred you to over what already went to that company.
  • note — only the log line. Without lead, company or email it is a line of your work log.

Someone not in the table yet is added with the address you give, also with email_replied, not_interested or out_of_office. Give their employer in lead.company and lead.company_domain; without a domain, the domain of a work address is used, so their emails count for that company in POST /outreach/check.

A step of your own that is none of these types (a call, a voicemail, a LinkedIn message you sent yourself, a visit) goes in action: a key you choose, such as call or linkedin_message, with action_label for how it reads (default from the key). Leave type out, or send note: an action is a note, so it stops nothing and changes no state, but the user can filter and count it on the Activity tab, and GET /outreach/activity filters on it. Use the same key every time.

external_id (your own id, e.g. the email message id) makes a repeated call count once: the same type about the same person or address. Another type or another person with the same external_id (a bounce that quotes the message id of your email) is logged on its own.

Authentication

Send Authorization: Bearer gtk_…. Create the key under Connect assistant → API keys. Every gtk_ key can call these routes, also while the Signals API is closed. A missing, invalid or revoked key is 401.

Body parameters

  • type string — What happened. Without it, action is required (then it is a note). One of: email_sent, email_replied, email_bounced, meeting_booked, not_interested, out_of_office, unsubscribed, referral, note.
  • action string — A step of your own: lowercase letters, digits and _, starting with a letter, up to 40 characters (call, voicemail, linkedin_message, visit). Only with type note or no type; not one of the types above.
  • action_label string — How the action reads in the table, up to 60 characters (Call). Default from the key.
  • lead string | object — The person: a row (sav_…, per_…), a LinkedIn URL or their address as a string; or { ref, lead_id, person_id, linkedin_url, name, first_name, last_name, company, company_domain, title, email }.
  • company string | object — Or the company: sav_…, cmp_…, its domain, page or name; or { ref, name, domain, linkedin_url }. Not together with lead: a person's employer goes in lead.company and lead.company_domain.
  • email string — The address you mailed or that wrote back. Alone it finds the person or company that has it.
  • subject string — The subject, up to 200 characters.
  • summary string — One or two sentences for the table, up to 1,000 characters.
  • text string — email_sent and email_replied: the email itself (without the quoted history), up to 20,000 characters; it shows in the Inbox (an email_sent also on Templates, above the email you planned next), and GET /outreach/activity?with_text=true gives it back.
  • occurred_at string — ISO time; default now. Never in the future.
  • campaign_id string — The campaign it belongs to.
  • external_id string — Your own id, to log it once (per type and person).
  • step integer — Which step of your sequence, from 1 (the first email) to 20.
  • template string — The name of your template, up to 80 characters.
  • thread_id string — The email thread, so replies and follow-ups stay together.
  • sentiment string — email_replied: how it reads. One of: positive, neutral, negative.
  • until string — out_of_office: the day they are back. email_replied with lead_status later: the day to recontact.
  • lead_status string — email_replied: what they said. One of: interested, meeting, later, not_interested.
  • referral object — referral: { name, title, linkedin_url, email, note } of the person they sent you to.
  • source_url string — Where you found it or did it: an http(s) address of up to 1,000 characters (the careers page, the annual accounts). Another source, such as a file or "Stripe", goes in summary.

Response

  • id string · required — The log line.
  • duplicate boolean · required — This activity (same external_id, type and person) was logged before; nothing changed. id and row are those of the earlier line.
  • row object | null · required — The row it is about: id, object, name.
  • added_to_table boolean · required — The row is new in Leads or Companies.
  • stopped_linkedin_sequences integer · required — LinkedIn sequences that stopped.
  • in_inbox boolean · required — The email shows in the Inbox.
  • referral_row object | null · required — referral: the row of the person they sent you to (id, name), to mail or update.
  • changes string[] · required — What changed on the row, in a few words.
  • dashboard_url string · required — The table in mySDR; for a work log line or a list of activities, the Activity tab.
  • logged / failed / results integer / integer / object[] — With activities: how many were logged, how many failed, and per item index, ok and the fields above or error.

Credits

Free. This call never uses credits.

Errors

  • 400 invalid_request — An unknown field or type, no type and no action, an action with another type than note or one that is a type itself, lead and company together, nobody to log it about (only a note can be about nobody), text on a type that is not an email, lead_status without email_replied, a source_url that is not an http(s) address, an email that is not a bare address, or a value that is not valid.
  • 404 not_found — The person, company or campaign is not in your organization, or no row has this address or its domain.
  • 409 invalid_state — The key has no user behind it, so mySDR cannot record who did it.
  • 401 authentication_error — The Authorization header is missing, or the key is unknown or revoked.
  • 403 permission_error — API access is not part of your plan, or your account is suspended.
  • 429 rate_limit_error — More than 60 requests per minute, or more than 3 at the same time, on one key. Wait for the Retry-After header.
  • 503 service_unavailable — The API is temporarily unavailable. Retry later.
  • 500 internal_error — Something went wrong on our side.

Example

cURL

curl -X POST "https://mysdr.app/api/v1/outreach/activity" \
  -H "Authorization: Bearer $MYSDR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "email_sent",
    "company": {
      "name": "Acme Logistics",
      "domain": "acme-logistics.example"
    },
    "email": "jobs@acme-logistics.example",
    "subject": "Your open roles",
    "text": "Hi team, …",
    "step": 1,
    "template": "step1_jobs_inbox",
    "thread_id": "thread-88",
    "external_id": "<msg-88@mail.example>"
  }'

JavaScript

const response = await fetch('https://mysdr.app/api/v1/outreach/activity', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.MYSDR_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    "type": "email_sent",
    "company": {
      "name": "Acme Logistics",
      "domain": "acme-logistics.example"
    },
    "email": "jobs@acme-logistics.example",
    "subject": "Your open roles",
    "text": "Hi team, …",
    "step": 1,
    "template": "step1_jobs_inbox",
    "thread_id": "thread-88",
    "external_id": "<msg-88@mail.example>"
  })
});
const body = await response.json();

Python

import json
import os
import urllib.request

payload = {
  "type": "email_sent",
  "company": {
    "name": "Acme Logistics",
    "domain": "acme-logistics.example"
  },
  "email": "jobs@acme-logistics.example",
  "subject": "Your open roles",
  "text": "Hi team, …",
  "step": 1,
  "template": "step1_jobs_inbox",
  "thread_id": "thread-88",
  "external_id": "<msg-88@mail.example>"
}

request = urllib.request.Request(
    "https://mysdr.app/api/v1/outreach/activity",
    headers={
    "Authorization": f"Bearer {os.environ['MYSDR_API_KEY']}",
    "Content-Type": "application/json"
    },
    data=json.dumps(payload).encode(),
    method="POST",
)
with urllib.request.urlopen(request) as response:
    body = json.load(response)

Example response

{
  "id": "a71c2f40-8d2e-4b91-9a10-3c5e7d9f0b21",
  "duplicate": false,
  "row": {
    "id": "sav_2b9e8d10-7f4a-4a51-8c2e-5d1f0a3b6c77",
    "object": "company",
    "name": "Acme Logistics"
  },
  "added_to_table": true,
  "stopped_linkedin_sequences": 0,
  "in_inbox": true,
  "referral_row": null,
  "changes": [
    "contact point updated"
  ],
  "dashboard_url": "https://mysdr.app/en/gtm/companies"
}