- A company in your ICP posted a VP of Engineering role on LinkedIn (Hiring signal)
- A lead’s company announced a Series B funding round (Funding signal)
- A founder published a Reddit post complaining about an alternative tool (Pain Point signal)
score of 1, 2, or 3: 1 is irrelevant and filtered out before reaching the feed, 2 is worth knowing, and 3 is easy-sell-now. Items below 2 are dropped by the scorer and never reach your feed.
Signal types
Signal types define what Puffle monitors. There are two kinds:Built-in types
Six pre-configured types are auto-seeded on first API call: Hiring, Funding, Leadership Change, Competitor Mentions (structured), plus Pain Point and Product Launch (keyword). They are always present on your account. You can tune their keywords, source selection, and enabled state, but not rename or delete them.
Custom types
Fully user-defined keyword types. You specify keywords, a free-form
prompt describing what to detect, and which sources to monitor. Accounts can have up to 20 signal types total across built-in and custom types.structuredConfig JSON object that is compiled into keywords and prompts at scan time. Hiring, Funding, Leadership Change, and Competitor Mentions are structured types, so you edit their config instead of raw keywords. Keyword types, including Pain Point, Product Launch, and custom types, use explicit keywords[] plus a natural-language prompt.
Data sources
Puffle’s active signal source registry is defined inlib/signals/registry.ts:
| Source | Identifier | Description |
|---|---|---|
| LinkedIn Posts | linkedin_posts | Public posts from company pages and individuals |
| LinkedIn Jobs | linkedin_jobs | Job postings on LinkedIn |
| News | news | News articles from the news waterfall |
| Hiring.cafe | hiring_cafe | Hiring signal data |
reddit | Posts and comments matching keywords | |
| Hacker News | hacker_news | HN posts |
| X / Twitter | x_twitter | Public tweets |
linkedin_jobs, hiring_cafe, and linkedin_posts only. Custom keyword types may target any source.
Signal lifecycle
createdAt, newest first by default, with optional sortBy=score. Dismissal flows through PATCH /signals with status: "dismissed". Dismissed signals are removed from the active feed but remain accessible with GET /signals?includeDismissed=true.
API surface
All endpoints below acceptAuthorization: Bearer pk_live_* and return the structured { error: { code, message } } shape on failure. See the API overview for authentication, pagination, and rate-limit details.
| Endpoint | Description |
|---|---|
| GET /signals | Read your signal feed (cursor-paginated) |
| PATCH /signals | Batch vote, comment, attach DM or comment drafts, or set status including dismissal |
| GET /signals/leads | People and companies aggregated from your feed |
| PATCH /signals/leads | Batch update lead workflow status (new, contacted, or dismissed) |
| GET /signals/types | List signal type configurations and auto-seed built-ins |
| POST /signals/types | Create a custom keyword type |
| PATCH /signals/types | Batch update existing types |
| DELETE /signals/types | Batch delete custom types; built-ins are protected |
| GET /signals/sources | Adapter registry merged with your enable state |
| PATCH /signals/sources | Toggle or configure sources; cascades to type rows |
| GET /signals/settings | Read scan schedule and paused flag |
| PATCH /signals/settings | Update scan schedule and paused flag |
| GET /signals/search | List freeform signal searches |
| POST /signals/search | Start a freeform signal search |
| GET /signals/analytics | Keyword performance and run history |
| POST /signals/generate | Generate DM or comment drafts for a batch of signals |
items: [...] array, and the response carries a { updated|deleted: string[], errors?: [{ id, error }] } envelope so partial failures surface per item without rolling back the rest of the batch.
Related concepts
Leads
Signals can point to leads worth adding.
Campaigns
Campaigns turn signal-informed priorities into outreach.