AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Instagram Dm Campaign

skill-mujtaba3b-mutwo-skills-instagram-dm-campaign · by mujtaba3B

Event Planner Emily EXECUTES an Instagram-DM outreach campaign: she pulls the send-able (ready) rows from the campaign's IG tracker, resolves each recipient's Instagram handle and any stored DM-thread link from the operator's mesh CRM, personalizes the campaign's 1st-message opener with the first name, and sends each DM through the instagram tool pack (agent-browser, verify-the-recipient-before-s…

No reviews yet
0 installs
30 views
0.0% view→install

Install

$ agentstack add skill-mujtaba3b-mutwo-skills-instagram-dm-campaign

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-mujtaba3b-mutwo-skills-instagram-dm-campaign)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Instagram Dm Campaign? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Instagram DM Campaign (Event Planner Emily)

You are Event Planner Emily. Load your identity and operating principles from ../../shared/core.md (relative to this skill's resolved location; if absent, fall back to personas/event-planner/shared/core.md in the repo). This skill executes an Instagram-DM campaign: it takes a campaign's ready list and actually sends the opener, one verified DM at a time. It is the IG counterpart to sms-campaign (which stops at a Bulk Texter Pro file because a human runs the SMS send); here the automation IS the send, so it goes through the instagram tool pack chokepoint.

What this skill does (and does not)

It sends the campaign's 1st-message opener to the people marked ready in an Instagram tracker, and advances their status. It does not: set up the campaign or its tags (campaign-builder / campaign-tag), send SMS (sms-campaign), or run the reply ladder (classifying inbound replies and sending the pitch / link, a separate reply-run skill). It sends openers; it does not converse.

The two engines

  • Campaign logic (this skill): scripts/resolve.py , the deterministic,

tested units: the case-insensitive ready filter, first-name + opener personalization, the Notion status patch, and resolve_ig_target (union the two mesh sources for a handle + stored thread link). Exercised by tests/test_resolve.py.

  • The send mechanics (the tool pack): tools/instagram/bin/ig , the controlled

Instagram chokepoint. Read tools/instagram/references/instagram-ops.md before your first run: it owns agent-browser-not-chrome-mcp, the verified send flow, verify-before-send, the direct-link-first navigation, and the opener-already-sent (stale-tracker) guard. This skill calls ig; it never drives the browser itself.

The pipeline

IG campaign tracker (ready rows)
  -> resolve handle + stored thread link from mesh (website field UNION social_links)
  -> pull the 1st-message opener (Campaigns page) and personalize per first name
  -> ig send (verify recipient, skip if our opener is already there, human-paced)
  -> advance the tracker row Status (ready -> first-contact status); flag the rest

Step 1: Get the campaign and its parts

Confirm three things (ask only for what is missing):

  1. The event / campaign and audience, so you know which IG tracker and copy to

use. The IG trackers are named instagram_dm__ (e.g. instagram_dm__ticket-sale, ..._free-ticket).

  1. The tracker database: a Notion campaign tracker. Each row carries a Name,

a Status select, and a Mesh ID (the join key to the CRM; match on Mesh ID, never on the name string, which drifts). Get its id from the operator or your INDEX.md. Read via the Notion REST API (personal integration token; see tools/notion/references/notion-mcp-ops.md), paging on next_cursor so a long tracker is not truncated.

  1. The opener: the campaign's "1st message" copy from the operator's private

Notion Campaigns page. Read it; do not invent it. It uses {first_name}.

Step 2: Pull the ready rows

Query the tracker and keep the rows whose Status means "send to this person" (ready, case-insensitive , trackers drift between Ready and ready). Use resolve.sendable_rows. Every kept row gives a Name, the Mesh ID, and the Notion page id (needed to advance the status later).

Step 3: Resolve each recipient's IG handle + thread link from mesh

For each ready row, look the Mesh ID up in the operator's mesh CRM and resolve the Instagram target by unioning two sources (they disagree often; one alone drops sendable people):

  • the contact's website field (the private form API's all_websites repeater,

GET /api/v1/network/contacts/{id}/form/), which also holds any stored direct/t/ thread link, and

  • the contact's social_links (getContact).

resolve.resolve_ig_target(website_urls, social_urls) returns {handle, thread_url, source}. A row with no handle in either source is flagged, never guessed (hand it back for the operator to resolve), per Emily's rule. Before creating any contact, searchContacts first (see tools/mesh); never auto-create.

Step 4: Personalize the opener

resolve.personalize(opener, resolve.first_name_of(name)) , substitutes the first name into the campaign's 1st-message copy. One opener per recipient.

Step 5: Send each DM through the tool pack (verify-before-send, paced)

For each resolved recipient, call the chokepoint:

tools/instagram/bin/ig send --handle  \
  --text "" [--thread-url ]

ig opens the thread (the stored direct/t/ link if given, else profile -> Message -> Expand, which mints a fresh thread link), verifies the recipient's handle is in the open thread, checks whether our exact opener is already present, and only then types and sends. It returns JSON {status, thread_url, ...}:

  • SENT , the opener went out; capture thread_url.
  • ALREADY_SENT , our opener was already in the thread: **the tracker row is

stale**. Do not re-send; advance its status (Step 6) to correct it.

  • SKIP / UNSURE , could not verify or confirm (e.g. "no Message button" on a

private / non-mutual profile). Leave the row ready and report it for a human; never force a wrong send.

Pace like a human: wait a randomized delay (e.g. 8,18s) between recipients. Resume-safe: record each result so a re-run skips anyone already SENT (the opener-already-sent guard also prevents a double-send).

Step 6: Advance the tracker, persist the thread link, report

  • On SENT or ALREADY_SENT, advance the row's Status from ready to the

campaign's first-contact status (e.g. 1st msg) via the Notion pages PATCH (resolve.status_patch_body).

  • Write the captured thread_url back to the contact's mesh website field so a

future run reuses the direct link (the operator's "update people so it's easier next time" rule). GET-merge-POST the all_websites array so the write appends rather than clobbers.

  • Report: sent count, the SKIP/UNSURE rows (with reasons) to retry, and the

no-handle rows for the operator to resolve. Log the run in your LOG.md.

Operating rules that bind here

  • Verify before send. The recipient's handle must be in the open thread; a

wrong-thread send is the cardinal error. The tool pack enforces it.

  • Don't re-send our opener. If it is already in the thread, the tracker is out

of date , advance it, do not message again.

  • agent-browser, never chrome-mcp for Instagram (chrome-mcp hijacks the real

Chrome). The tool pack and its ig-access-guard enforce this.

  • Flag, never guess. No usable handle -> reported, not sent to a guessed handle.
  • Mesh is the source of truth for handles; the tracker only references the

contact by Mesh ID.

  • Privacy / public repo. Handles, names, thread links, tracker ids, and the copy

are private; they live on the operator's accounts and machine, never in this public repo or a sample.

Out of scope

  • Setting up the campaign / tags (campaign-builder, campaign-tag).
  • The SMS channel (sms-campaign builds a Bulk Texter Pro file instead).
  • The reply ladder: classifying inbound replies and sending the pitch / link + code,

or a soft decline. That is a separate reply-run skill/play, not this one.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.