-
Notifications
You must be signed in to change notification settings - Fork 9
[ENG-213] Fern Changelog Automation #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| name: API Changelog Automation | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - 'fern/definition/**/*.yml' | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| generate-diff: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '20' | ||
|
|
||
| - name: Install Fern CLI | ||
| run: npm install -g fern-api | ||
|
|
||
| - name: Generate current OpenAPI spec | ||
| run: | | ||
| cd fern && fern export ../current-openapi.json | ||
| continue-on-error: true | ||
|
|
||
| - name: Generate base OpenAPI spec | ||
| run: | | ||
| git checkout ${{ github.event.pull_request.base.sha }} | ||
| cd fern && fern export ../base-openapi.json || echo '{}' > ../base-openapi.json | ||
| git checkout ${{ github.event.pull_request.head.sha }} | ||
| continue-on-error: true | ||
|
|
||
| - name: Run oasdiff changelog | ||
| id: oasdiff | ||
| uses: oasdiff/oasdiff-action/changelog@main | ||
| with: | ||
| base: base-openapi.json | ||
| revision: current-openapi.json | ||
| format: markdown | ||
| fail-on-diff: false | ||
| continue-on-error: true | ||
|
|
||
| - name: Check if changes detected | ||
| id: check_changes | ||
| run: | | ||
| if [ -n "${{ steps.oasdiff.outputs.changelog }}" ]; then | ||
| echo "has_changes=true" >> $GITHUB_OUTPUT | ||
| echo "breaking_count=${{ steps.oasdiff.outputs.breaking }}" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "has_changes=false" >> $GITHUB_OUTPUT | ||
| echo "breaking_count=0" >> $GITHUB_OUTPUT | ||
| fi | ||
|
|
||
| - name: Write changelog diff for local preview | ||
| if: steps.check_changes.outputs.has_changes == 'true' | ||
| env: | ||
| CHANGELOG_DIFF: ${{ steps.oasdiff.outputs.changelog }} | ||
| run: | | ||
| node -e "require('fs').writeFileSync('changelog-diff.md', process.env.CHANGELOG_DIFF || '')" | ||
|
|
||
| - name: Upload changelog diff for local preview | ||
| if: steps.check_changes.outputs.has_changes == 'true' | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: api-changelog-diff | ||
| path: changelog-diff.md | ||
| retention-days: 7 | ||
|
|
||
| - name: Comment on PR with diff | ||
| if: steps.check_changes.outputs.has_changes == 'true' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const diff = `${{ steps.oasdiff.outputs.changelog }}`; | ||
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const breaking = ${{ steps.check_changes.outputs.breaking_count }} > 0; | ||
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const body = `## ${breaking ? '🚨 Breaking' : '✨'} API Changes\n\n\`\`\`markdown\n${diff}\n\`\`\`\n\n💡 Download \`api-changelog-diff\` artifact or tag @Fern Writer in #api-changelog for changelog.`; | ||
| await github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body }); | ||
|
|
||
| - name: Post to Slack (if configured) | ||
| if: steps.check_changes.outputs.has_changes == 'true' | ||
| continue-on-error: true | ||
| env: | ||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||
| uses: slackapi/slack-github-action@v1.26.0 | ||
| with: | ||
| channel-id: ${{ secrets.SLACK_CHANGELOG_CHANNEL }} | ||
| payload: | | ||
| {"text": "<!here> Please @fern-writer to generate a changelog for this PR: ${{ github.event.pull_request.html_url }}\n\nReply to this message with \"produce a changelog for this\" (and @fern-writer) to trigger the bot."} | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| .env | ||
| **/.preview/ | ||
| **/generated/ | ||
| .DS_Store | ||
| *-openapi.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # AgentMail Changelog Guidelines | ||
|
|
||
| Instructions for creating changelog entries (e.g. when tagged in Slack or writing manually). One source of truth for process and style. | ||
|
|
||
| ## When you're invoked | ||
|
|
||
| - **API changes:** A PR that touches `fern/definition/**` triggers a GitHub Action. It posts an oasdiff (technical diff) on the PR and uploads it as the `api-changelog-diff` artifact. Someone may tag you with that PR or paste the diff. | ||
| - **Manual:** For non-API changes, create `fern/changelog/YYYY-MM-DD.mdx` and follow the structure below. Use `fern/changelog/TEMPLATE.mdx` as reference. | ||
| - **Output:** Create or edit files in `fern/changelog/`. File name: `YYYY-MM-DD.mdx` (same day = add suffix e.g. `2026-01-30-metrics.mdx`). | ||
|
|
||
| ## Required structure | ||
|
|
||
| 1. **Summary** (2–3 sentences): What changed and **user benefit**. Not "New GET /foo" — e.g. "Monitor X in real-time so agents can…" | ||
| 2. **What's new?** Bullets: new endpoints, features, or capabilities (with paths/params). | ||
| 3. **Breaking changes** (if any): ⚠️ header, clear explanation, before/after code, timeline if deprecated. | ||
| 4. **Use cases** (2–4): "Build agents that…" — what users can do with this. | ||
| 5. **SDK Updates:** Links to Python and Node SDK releases (include when documenting an SDK release). | ||
| 6. **Code block:** When it helps (bar is low), include **both Python and TypeScript** in a single toggle. Use `<CodeBlocks>` (plural) with two fenced blocks: ` ```python title="Python" ` and ` ```typescript title="TypeScript" `. When included: runnable (imports + client), < 15 lines per language. Use realistic IDs. **Code comments: use lowercase** (e.g. `# create a draft` not `# Create a draft`). | ||
| 7. **Note:** One call-to-action link to API reference or a guide. | ||
|
|
||
| ## Voice and terms | ||
|
|
||
| - User-focused, action-oriented, specific. Use "agents" (not "bots"), "inboxes" (not "mailboxes"), "deliverability", "bounce rate." | ||
| - Tags: 2–4 total. API area: `inboxes-api`, `messages-api`, `drafts-api`, `domains-api`, `webhooks`, `websockets`, `pods-api`, `metrics-api`, etc. Change type: `new-feature`, `breaking-change`, `enhancement`, `bug-fix`. Deliverable: `sdk`, `docs`. | ||
|
|
||
| ## Format (for predictable output) | ||
|
|
||
| - **Frontmatter:** Exactly `---` then `tags: ["tag1", "tag2", ...]` then `---`. Tags are a YAML array of strings. | ||
| - **Section headers:** Use `## Summary`, `### What's new?`, `### Use cases`. Add `### Breaking changes` only when needed. | ||
| - **Components:** Use `<CodeBlocks>` (plural) with two fenced blocks: ` ```python title="Python" ` … ` ``` ` and ` ```typescript title="TypeScript" ` … ` ``` ` so readers get a Python | TypeScript tab toggle. Use `<Note>` with a single paragraph and optional link inside. No other custom components. | ||
| - **Links:** Use `https://docs.agentmail.to/...` (e.g. `/api-reference/metrics`, `/core-concepts/pods`, `/webhooks/webhooks-overview`). No relative links. | ||
| - **Date:** Use today's date for new entries (`YYYY-MM-DD.mdx`) unless the user specifies a date. | ||
| - **Don't:** Invent endpoints or params — only document what's in the diff/API. Avoid raw `<` in prose (e.g. write "under 100ms" not "<100ms") so MDX doesn't break. | ||
|
|
||
| ## Minimal example (mirror this structure) | ||
|
|
||
| - Frontmatter: `---` then `tags: ["webhooks", "new-feature", "sdk"]` then `---` | ||
| - Sections in order: `## Summary` → `### What's new?` → `### Use cases` → `<CodeBlocks>` with ` ```python title="Python" ` and ` ```typescript title="TypeScript" ` (same example in both; runnable, imports + client) → `<Note>` with one sentence and link to `https://docs.agentmail.to/...` | ||
| - Add `### Breaking changes` only when applicable (⚠️ + before/after code). Add SDK Updates section when documenting an SDK release. | ||
|
|
||
| ## Checklist before finalizing | ||
|
|
||
| - Summary states user benefit. Code is runnable. Links valid. Breaking changes have before/after. Tags accurate. Technical accuracy matches API definition. | ||
|
|
||
| ## Slack flow (API changelog) | ||
|
|
||
| When the API Changelog workflow detects changes, it posts to your changelog Slack channel (if `SLACK_BOT_TOKEN` and `SLACK_CHANGELOG_CHANNEL` are set): | ||
|
|
||
| - **Message:** `@here` + “Please @fern-writer to generate a changelog for this PR” + link. It tells people to reply with “produce a changelog for this” (and @fern-writer). | ||
| - **Your move:** Reply to that message with “produce a changelog for this” and @fern-writer. The bot uses the PR link in the parent message as context (and can use the PR’s `api-changelog-diff` artifact or the bot comment on the PR for the diff). | ||
| - **To actually ping the bot in Slack:** If “@fern-writer” in the message doesn’t notify the bot, replace it in the workflow with the bot’s Slack user ID, e.g. `<@U01234ABCD>` (use a secret like `SLACK_FERN_WRITER_USER_ID` and put `<@${{ secrets.SLACK_FERN_WRITER_USER_ID }}>` in the payload). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| --- | ||
| tags: ["pods-api", "new-feature", "sdk", "collaboration"] | ||
| --- | ||
|
|
||
| ## Summary | ||
|
|
||
| Introducing **Pods** - team collaboration spaces for AgentMail. Share inboxes, domains, and resources across your organization while maintaining granular control. Perfect for teams building multi-agent email systems that need organized resource management. | ||
|
|
||
| ### What's new? | ||
|
|
||
| **New endpoints:** | ||
| - `POST /pods` - Create a new pod (team workspace) | ||
| - `GET /pods` - List all pods in your organization | ||
| - `GET /pods/{pod_id}` - Get pod details | ||
| - `DELETE /pods/{pod_id}` - Delete a pod | ||
| - `POST /pods/{pod_id}/inboxes` - Create inbox within a pod | ||
| - `POST /pods/{pod_id}/domains` - Add custom domain to a pod | ||
| - `GET /pods/{pod_id}/threads` - List threads within a pod | ||
| - `GET /pods/{pod_id}/metrics` - Get metrics for a pod | ||
|
|
||
| **Pod features:** | ||
| - Shared inbox access across team members | ||
| - Per-pod domain configuration | ||
| - Isolated metrics and analytics per pod | ||
| - Organized resource hierarchy | ||
|
|
||
| ### Use cases | ||
|
|
||
| Build systems where: | ||
| - Multiple agents share email infrastructure | ||
| - Different teams manage their own inboxes independently | ||
| - Resources are organized by department or project | ||
| - Analytics are tracked per team workspace | ||
| - Billing and usage can be attributed to specific teams | ||
|
|
||
| <CodeBlocks> | ||
| ```python title="Python" | ||
| from agentmail import AgentMail | ||
|
|
||
| client = AgentMail(api_key="your-api-key") | ||
|
|
||
| # create a pod for your sales team | ||
| pod = client.pods.create( | ||
| name="Sales Team", | ||
| description="Shared resources for sales agents" | ||
| ) | ||
|
|
||
| # create an inbox in the pod | ||
| inbox = client.pods.inboxes.create( | ||
| pod_id=pod.pod_id, | ||
| inbox_id="sales@example.com" | ||
| ) | ||
|
|
||
| # list all pods | ||
| pods = client.pods.list() | ||
| for pod in pods.pods: | ||
| print(f"Pod: {pod.name} ({len(pod.inbox_ids)} inboxes)") | ||
| ``` | ||
|
|
||
| ```typescript title="TypeScript" | ||
| import { AgentMail } from "agentmail"; | ||
|
|
||
| const client = new AgentMail({ apiKey: "your-api-key" }); | ||
|
|
||
| // create a pod for your sales team | ||
| const pod = await client.pods.create({ | ||
| name: "Sales Team", | ||
| description: "Shared resources for sales agents", | ||
| }); | ||
|
|
||
| // create an inbox in the pod | ||
| await client.pods.inboxes.create(pod.podId, "sales@example.com"); | ||
|
|
||
| // list all pods | ||
| const { pods } = await client.pods.list(); | ||
| for (const p of pods) { | ||
| console.log(`Pod: ${p.name} (${p.inboxIds?.length ?? 0} inboxes)`); | ||
| } | ||
| ``` | ||
| </CodeBlocks> | ||
|
|
||
| <Note> | ||
| Learn more about organizing teams with [Pods](https://docs.agentmail.to/core-concepts/pods) in our documentation. | ||
| </Note> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| --- | ||
| tags: ["websockets", "new-feature", "sdk", "real-time"] | ||
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| --- | ||
|
|
||
| ## Summary | ||
|
|
||
| Introducing **WebSocket Streaming** - receive email events in real-time as they happen. Build reactive agents that respond instantly to new messages, deliveries, and bounces without polling. Perfect for building interactive, event-driven email experiences. | ||
|
|
||
| ### What's new? | ||
|
|
||
| **WebSocket endpoint:** | ||
| - `wss://ws.agentmail.to/v0` - Real-time event streaming | ||
|
|
||
| **Events streamed:** | ||
| - `message.received` - New inbound email detected | ||
| - `message.sent` - Outbound email sent successfully | ||
| - `message.delivered` - Delivery confirmed by recipient server | ||
| - `message.bounced` - Bounce detected (permanent or temporary) | ||
| - `message.complained` - Spam complaint received | ||
|
|
||
| **Connection features:** | ||
| - JWT-based authentication for secure connections | ||
| - Automatic reconnection with exponential backoff | ||
| - Event filtering by inbox for targeted subscriptions | ||
| - Low-latency delivery (under 100ms typical) | ||
| - Support for thousands of concurrent connections | ||
|
|
||
| ### Use cases | ||
|
|
||
| Build agents that: | ||
| - Respond to emails within seconds of receipt | ||
| - Monitor deliverability in real-time across all inboxes | ||
| - Trigger workflows instantly on specific events | ||
| - Build interactive conversational email experiences | ||
| - Scale to handle high-volume email operations | ||
| - React to bounces and complaints immediately | ||
|
|
||
| <CodeBlock language="python"> | ||
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ```python | ||
| from agentmail import AgentMail | ||
|
|
||
| client = AgentMail(api_key="your-api-key") | ||
|
|
||
| # subscribe to events for an inbox | ||
| async with client.websockets.subscribe( | ||
| inbox_id="support@example.com" | ||
| ) as ws: | ||
| async for event in ws: | ||
| if event.type == "message.received": | ||
| print(f"New email from: {event.data.from_}") | ||
|
|
||
| # process and respond immediately | ||
| response = await generate_response(event.data.text) | ||
|
|
||
| # reply instantly | ||
| await client.messages.reply( | ||
| message_id=event.data.message_id, | ||
| text=response | ||
| ) | ||
| ``` | ||
| </CodeBlock> | ||
|
|
||
| <Note> | ||
| Get started with [WebSocket Streaming](https://docs.agentmail.to/websockets) to build real-time email agents. | ||
| </Note> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,31 @@ | ||
| --- | ||
| tags: ["metrics-api", "new-feature", "sdk"] | ||
| --- | ||
|
|
||
| ## Summary | ||
|
|
||
| We're excited to introduce **Metrics Endpoints** - two new powerful endpoints that give you deep insights into your email deliverability and agent performance. Track critical events like bounces, deliveries, rejections, and complaints with detailed timestamps. | ||
| We're excited to introduce **Metrics Endpoints** - two new powerful endpoints that give you deep insights into your email deliverability and agent performance. Track critical events like bounces, deliveries, rejections, and complaints with detailed timestamps to build smarter, self-optimizing email agents. | ||
|
|
||
| ### What's new? | ||
|
|
||
| New endpoints: | ||
|
|
||
| **New endpoints:** | ||
| - `GET /metrics` - Get comprehensive metrics across all your inboxes | ||
| - `GET /inboxes/{inbox_id}/metrics` - Get metrics for a specific inbox | ||
|
|
||
| Build smarter agents that monitor their own bounce rates, optimize send timing, and automatically adjust behavior based on deliverability metrics. This unlocks exciting possibilities for self-optimizing agents that can pause campaigns when performance drops or implement intelligent retry strategies. | ||
| **Metrics tracked:** | ||
| - Delivery events: sent, delivered, bounced, rejected | ||
| - Error tracking: complaints, spam reports | ||
| - Time-series data with detailed timestamps | ||
|
|
||
| ### Use cases | ||
|
|
||
| Build agents that: | ||
| - Monitor their own bounce rates in real-time | ||
| - Optimize send timing based on historical performance | ||
| - Automatically adjust behavior based on deliverability metrics | ||
| - Pause campaigns when performance drops below thresholds | ||
| - Implement intelligent retry strategies for better inbox placement | ||
|
|
||
| <Note> | ||
| Ready to build smarter agents? Check out our [metrics | ||
| documentation](https://docs.agentmail.to/api-reference/metrics) to get | ||
| started. | ||
| Ready to build smarter agents? Check out our [Metrics API documentation](https://docs.agentmail.to/api-reference/metrics) to get started. | ||
| </Note> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.