API reference/Outreach/Dashboards

.md

Save a dashboard

Builds a dashboard, or updates it: the same key again changes what you send and keeps what you leave out, and blocks replaces all blocks. Its tab shows in mySDR right away. New blocks are computed first: a filter on one of your own signals or a colleague the workspace does not know is a 400, and nothing is saved. The reply says what each block shows now (results): check it matches what the user asked, and give the user dashboard_url. At most 10 dashboards and 12 blocks each.

POSThttps://mysdr.app/api/v1/dashboards

Blocks, top to bottom; consecutive metrics sit side by side, consecutive charts two by two, a table and a note take the full width.

  • metric: one number. With source leads or companies, how many rows match filters; with activity, how many lines of the log in the period. divide_by (the same filters, for activity without period) makes it a percentage, e.g. a reply rate. compare: true (activity only) adds the period of the same length just before.
  • chart: on leads or companies, rows per group_by (lifecycle, status, lead_status, outreach, stage, owner, source, country, company, signal; companies has industry instead of company; also field.<key> for one of your own fields), the biggest first, at most 12 bars plus Other. On activity, group_by day or week draws the log over the period (at most 5 series plus Other), type, campaign or by (the key that logged it) draws bars.
  • table: rows with the columns you pick (up to 8; leads: name, title, company, location, lifecycle, outreach, lead_status, next_action, stage, owner, signals, last_signal_at, added_at, email, note; companies: name, domain, industry, country, employees, leads, lifecycle, lead_status, next_action, stage, owner, signals, technologies, funding, last_seen_at; both also field.<key>), sort (by a column, order asc or desc) and limit (1 to 100, default 25). On activity the columns are those of the Activity tab, newest first.
  • note: text, plain, up to 2000 characters, line breaks kept.
  • linkedin_limits: the LinkedIn limits of the user who looks, live: invites today and in the last 7 days, new sequences today (each used of its limit), a pause and the warm-up. No source or filters; title is optional. GET /outreach/linkedin and a start of a sequence suggest a dashboard with it in dashboard_offer.

Filters. On leads and companies, the names of GET /tables/leads in JSON: q, lifecycle, status, lead_status, due, campaign_id, outreach, source, stage, owner, do_not_contact, agent_signal (lists as arrays) and field_filters ([{"key": "hubspot", "equals": "open_deal"}], with equals, gte, lte or exists). On activity: types (email_sent, email_replied, email_bounced, meeting_booked, not_interested, out_of_office, unsubscribed, referral, note, status_changed, stage_changed, owner_changed, signal_hit, linkedin_invite_sent, linkedin_invite_accepted, linkedin_invite_declined, linkedin_invite_withdrawn, linkedin_message_sent, linkedin_post_liked, linkedin_like_skipped, linkedin_message_skipped, linkedin_replied, linkedin_stopped), actions (your own actions, e.g. call; with types, a line matches either), campaign_id and period (today, this_week, this_month, last_7_days, last_30_days, last_90_days, all; Brussels calendar days, default last_30_days).

A dashboard the user asks your agent to build: it shows as its own tab on the Dashboard in mySDR (the tabs are only dashboards; mySDR builds the first one, Overview with the key mysdr_overview, from the first data) and refreshes by itself. You describe it; mySDR draws it from the live Leads and Companies tables and the activity log, with the same filters as GET /tables/leads, so a block counts the same rows as the table. No HTML or code: a dashboard is a list of blocks of five types. Changing or removing it is something the user asks your agent, not a button in mySDR. These routes are the tools save_dashboard, list_dashboards, get_dashboard and delete_dashboard of the assistant server.

Authentication#

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

Body parameters#

  • key string · required — Your own name for the dashboard: lowercase letters, digits and _, starting with a letter, up to 40 characters (Pipeline Q4 becomes pipeline_q4). Use the same key every time.
  • name string — The label of its tab, up to 40 characters. Required the first time.
  • description string | null — One line under the name, up to 200 characters. null clears it.
  • position integer | null — Where its tab goes among the dashboards: 1 to 99, 1 = first; null = last (in the order they came).
  • blocks object[] — 1 to 12 blocks, top to bottom. Required the first time; replaces all blocks.
  • blocks[].type string · required — What the block is. One of: metric, chart, table, note, linkedin_limits.
  • blocks[].title string — What it shows, above it, up to 80 characters. Required except for a note and linkedin_limits.
  • blocks[].source string — The Leads or Companies table, or the activity log. Not for a note or linkedin_limits. One of: leads, companies, activity.
  • blocks[].filters object — Which rows or log lines count (see Filters).
  • blocks[].divide_by object — metric: a percentage of filters over these.
  • blocks[].compare boolean — metric on activity: also the period just before.
  • blocks[].group_by string — chart: what the bars or the line group on. Required for a chart.
  • blocks[].columns string[] — table on leads or companies: the columns, in order.
  • blocks[].sort object — table on leads or companies: by (a column) and order.
  • blocks[].limit integer — table: how many rows it shows, 1 to 100.
  • blocks[].text string — note: the text. Required for a note.

Response#

  • object string · required — Always dashboard. One of: dashboard.
  • key string · required — Your own name for it: lowercase letters, digits and _.
  • name string · required — The label of its tab.
  • description string | null · required — The line under the name.
  • position integer | null · required — Where its tab goes among the dashboards (1 = first); null = last.
  • blocks object[] · required — The blocks as saved.
  • via string | null · required — The name of the key that last saved it ("Claude").
  • created_at string · required — When it was created.
  • updated_at string · required — When it last changed.
  • dashboard_url string · required — Its tab in mySDR.
  • computed_at string · required — When the blocks were computed.
  • results object[] · required — Per block, in order: index, type, title, source, and result or error. A metric gives value (null when a percentage has nothing to divide by), unit (count or percent), previous, part and whole; a chart kind bars with groups (key, label, value) and total, or kind series with interval, series and buckets (start, current, values); a table columns, rows (id, href, cells) and total; a note its text. capped = there is more than the dashboard reads at once (5,000 leads, or 5,000 lines of the log per source), so the number is at least this. error (type, message, param) says why a block shows nothing, e.g. a filter on a signal that is gone.
  • created boolean · required — True when this call created it; false when it updated one.

Credits#

Free. This call never uses credits.

Errors#

  • 400 invalid_request — No key, a key that is not valid, no name or blocks the first time, a block that is not valid (param names it, e.g. blocks[2].group_by), a filter on something the workspace does not have, or an unknown field.
  • 409 invalid_state — The workspace has 10 dashboards already: delete one first.
  • 503 service_unavailable — Dashboards are not available in this environment yet.
  • 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

POST/dashboards
curl -X POST "https://mysdr.app/api/v1/dashboards" \
  -H "Authorization: Bearer $MYSDR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "pipeline_q4",
    "name": "Pipeline Q4",
    "description": "Where Q4 stands, every morning.",
    "position": 1,
    "blocks": [
      {
        "type": "metric",
        "title": "Meetings booked",
        "source": "leads",
        "filters": {
          "lifecycle": [
            "meeting"
          ]
        }
      },
      {
        "type": "metric",
        "title": "Reply rate, 30 days",
        "source": "activity",
        "filters": {
          "types": [
            "email_replied",
            "linkedin_replied"
          ]
        },
        "divide_by": {
          "types": [
            "email_sent",
            "linkedin_message_sent"
          ]
        },
        "compare": true
      },
      {
        "type": "chart",
        "title": "Leads per stage",
        "source": "leads",
        "filters": {
          "lifecycle": [
            "open"
          ]
        },
        "group_by": "stage"
      },
      {
        "type": "chart",
        "title": "Emails and replies per day",
        "source": "activity",
        "filters": {
          "types": [
            "email_sent",
            "email_replied"
          ],
          "period": "last_30_days"
        },
        "group_by": "day"
      },
      {
        "type": "table",
        "title": "Open deals in HubSpot",
        "source": "leads",
        "filters": {
          "field_filters": [
            {
              "key": "hubspot",
              "equals": "open_deal"
            }
          ]
        },
        "columns": [
          "name",
          "company",
          "lifecycle",
          "next_action",
          "field.hubspot"
        ],
        "sort": {
          "by": "next_action",
          "order": "asc"
        },
        "limit": 25
      }
    ]
  }'
Response
{
  "object": "dashboard",
  "key": "pipeline_q4",
  "name": "Pipeline Q4",
  "description": "Where Q4 stands, every morning.",
  "position": 1,
  "blocks": [
    {
      "type": "metric",
      "title": "Meetings booked",
      "source": "leads",
      "filters": {
        "lifecycle": [
          "meeting"
        ]
      }
    },
    {
      "type": "metric",
      "title": "Reply rate, 30 days",
      "source": "activity",
      "filters": {
        "types": [
          "email_replied",
          "linkedin_replied"
        ]
      },
      "divide_by": {
        "types": [
          "email_sent",
          "linkedin_message_sent"
        ]
      },
      "compare": true
    },
    {
      "type": "chart",
      "title": "Leads per stage",
      "source": "leads",
      "filters": {
        "lifecycle": [
          "open"
        ]
      },
      "group_by": "stage"
    },
    {
      "type": "chart",
      "title": "Emails and replies per day",
      "source": "activity",
      "filters": {
        "types": [
          "email_sent",
          "email_replied"
        ],
        "period": "last_30_days"
      },
      "group_by": "day"
    },
    {
      "type": "table",
      "title": "Open deals in HubSpot",
      "source": "leads",
      "filters": {
        "field_filters": [
          {
            "key": "hubspot",
            "equals": "open_deal"
          }
        ]
      },
      "columns": [
        "name",
        "company",
        "lifecycle",
        "next_action",
        "field.hubspot"
      ],
      "sort": {
        "by": "next_action",
        "order": "asc"
      },
      "limit": 25
    }
  ],
  "via": "Claude",
  "created_at": "2026-10-27T08:00:00.000Z",
  "updated_at": "2026-10-27T08:00:00.000Z",
  "dashboard_url": "https://mysdr.app/en/gtm/views/pipeline_q4",
  "computed_at": "2026-10-27T08:00:01.000Z",
  "results": [
    {
      "index": 0,
      "type": "metric",
      "title": "Meetings booked",
      "source": "leads",
      "result": {
        "type": "metric",
        "value": 4,
        "unit": "count",
        "previous": null,
        "part": null,
        "whole": null,
        "period": null,
        "capped": false
      },
      "error": null
    },
    {
      "index": 1,
      "type": "metric",
      "title": "Reply rate, 30 days",
      "source": "activity",
      "result": {
        "type": "metric",
        "value": 12.5,
        "unit": "percent",
        "previous": 9.1,
        "part": 10,
        "whole": 80,
        "period": "last_30_days",
        "capped": false
      },
      "error": null
    }
  ],
  "created": true
}