Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
148009 stories
·
33 followers

API Security Best Practices: A Developer’s Guide to Protecting Your APIs

1 Share

This guide explains how to secure an API in production. You’ll learn:

  • The most important API security best practices
  • Common vulnerabilities like Broken Object Level Authorization (BOLA)
  • How to implement authentication, authorization, rate limiting, and input validation correctly
  • How to test and verify your API security controls before release


Try Postman today →

APIs are the backbone of authentication, payments, mobile apps, partner integrations, and internal microservices. This also makes them prime targets for attackers.

When developing a REST API for either internal use or for public consumption, it’s crucial to have layered security controls implemented from the start. A single misconfigured endpoint can expose sensitive data, enable injection attacks, or allow unauthorized users to access resources they shouldn’t.

No single control is enough. Effective API security comes from combining multiple defenses that work together.

How do you secure an API?

To secure an API, use a combination of defensive strategies: require HTTPS for all traffic, set up strong authentication (OAuth 2.0, JWTs), conduct thorough input validation, implement rate limiting, practice the principle of least privilege, and watch for abnormal API behavior. Effective API security requires multiple integrated practices, not just one.

Practice What it protects against Priority
HTTPS/TLS encryption Data interception, man-in-the-middle attacks Required
Authentication (OAuth 2.0, JWT) Unauthorized access, identity spoofing Required
Authorization and access control Privilege escalation, data leakage Required
Input validation Injection attacks, malformed requests Required
Rate limiting Brute-force attacks, DDoS, abuse High
API key management Credential theft, unauthorized usage High
Logging and monitoring Undetected breaches, slow incident response High
Security testing Unknown vulnerabilities, regressions High

Why API security matters

APIs expose application logic and data to external consumers by design. Every endpoint is a potential attack surface that you’re deliberately making accessible. The more APIs you manage, the larger that surface becomes, and the harder it is to enforce security consistently across all of them.

The consequences of poor API security are well documented: broken authentication, excessive data exposure, and missing access controls consistently appear in the OWASP API Security Top 10, a widely referenced list of the most critical API vulnerabilities. The risks involved are real. Misconfigured APIs have led to breaches exposing millions of user records, unauthorized access to internal systems, and financial losses from abuse of unprotected endpoints.

The good news: most API vulnerabilities are preventable with the right practices applied consistently throughout the development lifecycle.

Encrypt all traffic with HTTPS

While TLS encryption is essential for external-facing APIs, most teams overlook its importance for internal traffic. Service-to-service communication in distributed systems faces interception risks, particularly in shared infrastructure or multi-tenant cloud environments where network traffic might breach trust boundaries.

# Enforce HTTPS in your API responses
Strict-Transport-Security: max-age=31536000; includeSubDomains

Use TLS 1.2 or higher (preferably TLS 1.3) and disable older protocols like SSLv3 and TLS 1.0. Configure your servers to reject unencrypted connections entirely rather than redirecting from HTTP to HTTPS, which still exposes the initial request.

For service-to-service communication, consider mutual TLS (mTLS), where both the client and server present certificates. This confirms identity from both sides, which is especially helpful in microservice setups to ensure internal callers are legitimate, beyond just encrypted connections.

Use strong authentication

The most common and preventable API vulnerability is weak or absent authentication. OAuth 2.0 with JWTs is the standard for production APIs, giving you delegated authorization flows with stateless token verification on every request.

POST /api/login HTTP/1.1
Content-Type: application/json

{
  "grant_type": "client_credentials",
  "client_id": "your_client_id",
  "client_secret": "your_client_secret"
}

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Then pass the token in the Authorization header:

GET /api/users/12345 HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

Key authentication practices:

Use short-lived access tokens (minutes to hours, not days or weeks) and refresh tokens for extended sessions.

Validate JWT signatures on every request. Never trust a token without verifying its signature, expiration, and issuer.

Store tokens securely. Keep access tokens in memory on the client side, not in localStorage or cookies without proper flags.

Never rely on API keys alone for authentication. API keys identify the calling application but don’t verify user identity. Use them alongside OAuth 2.0, not as a replacement.

Implement proper authorization

The top vulnerability on the OWASP API Security Top 10 list is Broken Object Level Authorization (BOLA), and it’s surprisingly common. An API causes this when it verifies user authentication but does not check if they have permission to access the requested resource.

# Vulnerable: No ownership check
GET /api/orders/789
# Any authenticated user can access any order

# Secure: Verify the requesting user owns the resource
GET /api/orders/789
# Server checks: Does the authenticated user own order 789?

When you configure authentication, apply the principle of least privilege. Grant the minimum permissions needed for each role or scope. A reporting dashboard doesn’t need write access to user records, and a mobile app shouldn’t have admin privileges.

Use Role-Based Access Control (RBAC) or Attribute-Based Access Control (ABAC) to enforce permissions consistently:

// Middleware example: Check user role before allowing access
function authorize(requiredRole) {
  return (req, res, next) => {
    if (req.user.role !== requiredRole) {
      return res.status(403).json({ error: "Forbidden" });
    }
    next();
  };
}

app.delete("/api/users/:id", authorize("admin"), deleteUser);

Validate and sanitize all input

The concept of input validation is easy, but ensuring it’s applied uniformly to every endpoint, parameter, header, and query string is challenging, particularly for growing APIs. This validation layer is designed to stop injection attacks (SQL, NoSQL, command injection), reject improperly formed payloads, and detect business logic exploits before they hit your application code.

// Validate input before processing
app.post("/api/users", (req, res) => {
  const { name, email, role } = req.body;

  // Check required fields exist
  if (!name || !email) {
    return res.status(400).json({ error: "Name and email are required" });
  }

  // Validate email format
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!emailRegex.test(email)) {
    return res.status(400).json({ error: "Invalid email format" });
  }

  // Whitelist allowed values
  const allowedRoles = ["viewer", "editor", "admin"];
  if (role && !allowedRoles.includes(role)) {
    return res.status(400).json({ error: "Invalid role" });
  }

  // Proceed with validated data
  createUser({ name, email, role: role || "viewer" });
});

Key validation practices:

  • Use allowlists over denylists. Define what’s accepted rather than trying to block every possible malicious input.
  • Validate data types, lengths, ranges, and formats. A user ID should be a number, not an arbitrary string.
  • Sanitize output as well as input. Encode data before including it in responses to prevent cross-site scripting (XSS) in API consumers that render HTML.
  • Use a schema validation library (like Joi, Zod, or JSON Schema) to enforce request structure consistently across endpoints.

Apply rate limiting and throttling

Rate limiting restricts the volume of requests a client can send over a defined period. Without it, your API is vulnerable to brute-force attacks, credential stuffing, denial-of-service (DoS) attacks, and general abuse from misbehaving clients.

HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 67
X-RateLimit-Reset: 1700000000

When a client exceeds the limit, return a 429 Too Many Requests status with a Retry-After header:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{
  "error": "Rate limit exceeded. Try again in 30 seconds."
}

Set different rate limits based on the endpoint’s sensitivity. For example, login endpoints and password reset flows should have stricter limits than read-only data endpoints. Consider per-user, per-IP, and per-API-key limits to prevent abuse from multiple angles.

When duplicate requests could cause actual harm to payment or transaction endpoints, use idempotency keys for secure retry handling.

POST /api/payments HTTP/1.1
Idempotency-Key: a1b2c3d4-unique-key
Content-Type: application/json

{
  "amount": 99.99,
  "currency": "USD"
}

Manage API keys and secrets securely

Careful handling of API keys, tokens, and credentials is essential at every stage, including generation, rotation, and revocation.

Never hardcode secrets in source code. This is one of the most common and most preventable security mistakes. Repository history retains keys even after you delete them from the current version.

# Bad: Hardcoded in source
API_KEY = "sk_live_abc123xyz789"

# Good: Loaded from environment variables
API_KEY = os.environ.get("API_KEY")

Key practices:

  • Store secrets in environment variables or a dedicated secrets manager (like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault).
  • Rotate API keys on a regular schedule and immediately after any suspected exposure.
  • Use scoped keys with the minimum permissions needed. A key that only needs read access shouldn’t have write privileges.
  • Set expiration dates on keys and tokens. Long-lived credentials increase the window of exposure if they’re compromised.
  • Monitor key usage for anomalies. A surge in requests from one key could signal a security breach.

Return only what’s needed

Oversharing data in API responses is a subtle but serious security risk. If your API returns full user objects when the client only needs a name and email, you’re unnecessarily exposing fields like internal IDs, roles, password hashes, or other sensitive attributes.

// Bad: Returns everything from the database
app.get("/api/users/:id", async (req, res) => {
  const user = await db.users.findById(req.params.id);
  res.json(user); // Includes password_hash, internal_notes, etc.
});

// Good: Return only what the client needs
app.get("/api/users/:id", async (req, res) => {
  const user = await db.users.findById(req.params.id);
  res.json({
    id: user.id,
    name: user.name,
    email: user.email,
    role: user.role
  });
});

This approach, also known as data minimization, extends to error messages. Detailed error responses give attackers insight into your API’s structure. Return enough information for legitimate clients to fix their requests, but avoid exposing stack traces, database details, or internal service names in production.

Log, monitor, and alert

You can’t protect what you can’t see. With extensive logging and monitoring, you can spot ongoing attacks, examine incidents later, and find patterns that identify potential threats.

What to log:

  • All authentication attempts (successful and failed)

  • Authorization failures (403 responses)

  • Unusual request patterns (high volume from a single source, requests to non-existent endpoints)

  • Changes to sensitive resources (user roles, payment methods, API keys)

  • Rate limit violations

What not to log: Full request bodies containing passwords, tokens, credit card numbers, or other sensitive data. Log enough to investigate without creating a new exposure point.

Set up alerts for anomalies. For instance, a sharp rise in 401 responses could mean a credential stuffing attack, and a cluster of 500 errors might reveal an exploit attempt on a specific endpoint.

Test your API security in Postman

Security practices only work if they’re verified. Postman makes it straightforward to test authentication, authorization, input validation, and error handling across your API.

Test authentication enforcement:

  1. Create a request to a protected endpoint without including an auth token.

  2. Verify that you receive a 401 Unauthorized response.

  3. Add a valid token and confirm the request succeeds.

  4. Send an expired or malformed token and confirm you get a 401.

Test authorization controls:

  1. Authenticate as a regular user.

  2. Atstructurestempt to access or modify a resource owned by a different user.

  3. Verify the API returns 403 Forbidden, not the resource data.

Test input validation:

  1. Send requests with missing required fields and verify you get 400 Bad Request with a clear error message.

  2. Send oversized payloads, special characters, and SQL injection strings to confirm the API rejects them.

  3. Check that error responses don’t leak internal details like stack traces or database structures.

Test rate limiting:

  1. Use Postman’s Collection Runner to send rapid sequential requests to a rate-limited endpoint.

  2. Verify the API returns 429 Too Many Requests after hitting the limit.

  3. Confirm the response includes Retry-After or rate limit headers.

Add assertions to your test scripts to automate these checks:

pm.test("Unauthorized request returns 401", function () {
  pm.response.to.have.status(401);
});

pm.test("Response does not contain sensitive fields", function () {
  const body = pm.response.json();
  pm.expect(body).to.not.have.property("password_hash");
  pm.expect(body).to.not.have.property("internal_notes");
});

Common API security mistakes to avoid

Relying on API keys as your only authentication. API keys identify applications, not users. They’re easily leaked in client-side code, browser history, or server logs. Pair them with proper user authentication like OAuth 2.0.

Returning too much data in responses. Don’t send entire database records when the client only needs two fields. Filter response data at the application layer to prevent accidental exposure of sensitive information.

Skipping authorization checks on individual resources. Verifying that a user is authenticated isn’t enough. Your API needs to verify permission for every resource requested, on every endpoint, without fail.

Inconsistent security policies across internal and external APIs. Internal APIs often get a pass on authentication, rate limiting, or input validation because they’re “not public-facing.” But lateral movement through internal APIs is a common attack pattern once an attacker gains initial access. Apply the same security baseline everywhere.

Using outdated dependencies. Vulnerabilities in third-party libraries are a common attack vector. Keep your dependencies updated and monitor security advisories for the frameworks and packages your API relies on.

Exposing detailed error messages in production. Stack traces and database errors help during development but give attackers a roadmap in production. Return generic error messages and log the details server-side.

API Security FAQs

Question Answer
What’s the most critical API security practice? Strong authentication and authorization on every endpoint.
Should internal APIs use HTTPS? Yes. Always encrypt API traffic, including service-to-service communication.
Are API keys enough for security? No. API keys identify applications but don’t authenticate users. Use them alongside OAuth 2.0 or JWTs.
What is BOLA? Broken Object Level Authorization, which is when an API doesn’t verify a user’s access to a specific resource. It’s the #1 OWASP API vulnerability.
How often should I rotate API keys? Regularly (every 60–90 days) and immediately after any suspected compromise.
What status code for rate limiting? 429 Too Many Requests, with a Retry-After header.
Should I validate input on the server? Always. Validate at the API layer regardless of what upstream clients or gateways do.
How can I test API security? Verify authentication, authorization, input validation, and rate limiting. Automate checks with test scripts in your CI/CD pipeline.

The post API Security Best Practices: A Developer’s Guide to Protecting Your APIs appeared first on Postman Blog.

Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

Making Code Easier to Understand with Mermaid Diagrams and CodeRush

1 Share
The author emphasizes the utility of CodeRush by DevExpress for refactoring, particularly its feature for embedding images in source code. Using Mermaid's flow diagrams, the author illustrates complex methods visually, enhancing code comprehension. Despite limitations in image handling, the combination of Mermaid and CodeRush promotes clear, maintainable code effectively.











Read the whole story
alvinashcraft
26 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Upgrade to SQL Server 2025 Enterprise Developer Edition

1 Share

Learn the steps to perform an SQL Server 2025 upgrade for the developer edition from older versions with our easy-to-follow guide.

The post Upgrade to SQL Server 2025 Enterprise Developer Edition appeared first on MSSQLTips.com.

Read the whole story
alvinashcraft
54 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Migrate your Semantic Kernel and AutoGen projects to Microsoft Agent Framework Release Candidate

1 Share

We’re thrilled to announce that Microsoft Agent Framework has reached Release Candidate status for both .NET and Python. Release Candidate is an important milestone on the road to General Availability — it means the API surface is stable, and all features that we intend to release with version 1.0 are complete. Now is the time to move your Semantic Kernel project to Microsoft Agent Framework and give us your feedback before final release. Whether you’re building a single helpful assistant or orchestrating a team of specialized agents, Agent Framework gives you a consistent, multi-language foundation to do it.

What is Microsoft Agent Framework?

Microsoft Agent Framework is a comprehensive, open-source framework for building, orchestrating, and deploying AI agents. It’s the successor to Semantic Kernel and AutoGen, and it provides a unified programming model across .NET and Python with:

  • Simple agent creation — go from zero to a working agent in just a few lines of code
  • Function tools — give agents the ability to call your code with type-safe tool definitions
  • Graph-based workflows — compose agents and functions into sequential, concurrent, handoff, and group chat patterns with streaming, checkpointing, and human-in-the-loop support
  • Multi-provider support — works with Microsoft Foundry, Azure OpenAI, OpenAI, GitHub Copilot, Anthropic Claude, AWS Bedrock, Ollama, and more
  • Interoperability — supports A2A (Agent-to-Agent), AG-UI, and MCP (Model Context Protocol) standards

Migration from Semantic Kernel and AutoGen

If you’ve been building agents with Semantic Kernel or AutoGen, Agent Framework is the natural next step. We’ve published detailed migration guides to help you transition:

Create Your First Agent

Getting started takes just a few lines of code. Here’s how to create a simple agent in both languages.

Python

pip install agent-framework --pre
import asyncio
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential


async def main():
    agent = AzureOpenAIResponsesClient(
        credential=AzureCliCredential(),
    ).as_agent(
        name="HaikuBot",
        instructions="You are an upbeat assistant that writes beautifully.",
    )

    print(await agent.run("Write a haiku about Microsoft Agent Framework."))

if __name__ == "__main__":
    asyncio.run(main())

.NET

dotnet add package Microsoft.Agents.AI.OpenAI --prerelease
dotnet add package Azure.Identity
using System.ClientModel.Primitives;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Responses;

// Replace <resource> and gpt-4.1 with your Azure OpenAI resource name and deployment name.
var agent = new OpenAIClient(
    new BearerTokenPolicy(new AzureCliCredential(), "https://ai.azure.com/.default"),
    new OpenAIClientOptions() { Endpoint = new Uri("https://<resource>.openai.azure.com/openai/v1") })
    .GetResponsesClient("gpt-4.1")
    .AsAIAgent(name: "HaikuBot", instructions: "You are an upbeat assistant that writes beautifully.");

Console.WriteLine(await agent.RunAsync("Write a haiku about Microsoft Agent Framework."));

That’s it — a working AI agent in a handful of lines. From here you can add function tools, sessions for multi-turn conversations, streaming responses, and more.

Multi-Agent Workflows

Single agents are powerful, but real-world applications often need multiple agents working together. Agent Framework ships with a workflow engine that lets you compose agents into orchestration patterns — sequential, concurrent, handoff, and group chat — all with streaming support built in.

Here’s a sequential workflow where a copywriter agent drafts a tagline and a reviewer agent provides feedback:

Python

pip install agent-framework-orchestrations --pre
import asyncio
from typing import cast

from agent_framework import Message
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import SequentialBuilder
from azure.identity import AzureCliCredential


async def main() -> None:
    client = AzureOpenAIChatClient(credential=AzureCliCredential())

    writer = client.as_agent(
        instructions="You are a concise copywriter. Provide a single, punchy marketing sentence based on the prompt.",
        name="writer",
    )

    reviewer = client.as_agent(
        instructions="You are a thoughtful reviewer. Give brief feedback on the previous assistant message.",
        name="reviewer",
    )

    # Build sequential workflow: writer -> reviewer
    workflow = SequentialBuilder(participants=[writer, reviewer]).build()

    # Run and collect outputs
    outputs: list[list[Message]] = []
    async for event in workflow.run("Write a tagline for a budget-friendly eBike.", stream=True):
        if event.type == "output":
            outputs.append(cast(list[Message], event.data))

    if outputs:
        for msg in outputs[-1]:
            name = msg.author_name or "user"
            print(f"[{name}]: {msg.text}")


if __name__ == "__main__":
    asyncio.run(main())

.NET

dotnet add package Microsoft.Agents.AI.Workflows --prerelease
using System.ClientModel.Primitives;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
using OpenAI;

// Replace <resource> and gpt-4.1 with your Azure OpenAI resource name and deployment name.
var chatClient = new OpenAIClient(
    new BearerTokenPolicy(new AzureCliCredential(), "https://ai.azure.com/.default"),
    new OpenAIClientOptions() { Endpoint = new Uri("https://<resource>.openai.azure.com/openai/v1") })
    .GetChatClient("gpt-4.1")
    .AsIChatClient();

ChatClientAgent writer = new(chatClient,
    "You are a concise copywriter. Provide a single, punchy marketing sentence based on the prompt.",
    "writer");

ChatClientAgent reviewer = new(chatClient,
    "You are a thoughtful reviewer. Give brief feedback on the previous assistant message.",
    "reviewer");

// Build sequential workflow: writer -> reviewer
Workflow workflow = AgentWorkflowBuilder.BuildSequential(writer, reviewer);

List<ChatMessage> messages = [new(ChatRole.User, "Write a tagline for a budget-friendly eBike.")];

await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, messages);

await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
{
    if (evt is AgentResponseUpdateEvent e)
    {
        Console.Write(e.Update.Text);
    }
}

What’s Next?

This Release Candidate represents an important step toward General Availability. We encourage you to try the framework and share your feedback — your input is invaluable as we finalize the release in the coming weeks.

For more information, check out our documentation and examples on GitHub, and install the latest packages from NuGet (.NET) or PyPI (Python).

The post Migrate your Semantic Kernel and AutoGen projects to Microsoft Agent Framework Release Candidate appeared first on Semantic Kernel.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Microsoft Agent Framework Reaches Release Candidate

1 Share

We’re happy to announce that Microsoft Agent Framework is now in Release Candidate status for both .NET and Python. Release Candidate is an important milestone on the road to General Availability — it means the API surface is stable, and all features that we intend to release with version 1.0 are complete. Whether you’re building a single helpful assistant or orchestrating a team of specialized agents, Agent Framework gives you a consistent, multi-language foundation to do it. Microsoft Agent Framework is the easy and most powerful way to build agents and agent systems using Microsoft Foundry or any model or AI service!

devui screenshot image

What is Microsoft Agent Framework?

Microsoft Agent Framework is a comprehensive, open-source framework for building, orchestrating, and deploying AI agents. It’s the successor to Semantic Kernel and AutoGen, and it provides a unified programming model across .NET and Python with:

  • Simple agent creation — go from zero to a working agent in just a few lines of code
  • Function tools — give agents the ability to call your code with type-safe tool definitions
  • Graph-based workflows — compose agents and functions into sequential, concurrent, handoff, and group chat patterns with streaming, checkpointing, and human-in-the-loop support
  • Multi-provider support — works with Microsoft Foundry, Azure OpenAI, OpenAI, GitHub Copilot, Anthropic Claude, AWS Bedrock, Ollama, and more
  • Interoperability — supports A2A (Agent-to-Agent), AG-UI, and MCP (Model Context Protocol) standards

Create Your First Agent

Getting started takes just a few lines of code. Here’s how to create a simple agent in both languages.

Python

pip install agent-framework --pre
import asyncio
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential


async def main():
    agent = AzureOpenAIResponsesClient(
        credential=AzureCliCredential(),
    ).as_agent(
        name="HaikuBot",
        instructions="You are an upbeat assistant that writes beautifully.",
    )

    print(await agent.run("Write a haiku about Microsoft Agent Framework."))

if __name__ == "__main__":
    asyncio.run(main())

.NET

dotnet add package Microsoft.Agents.AI.OpenAI --prerelease
dotnet add package Azure.Identity
using System.ClientModel.Primitives;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Responses;

// Replace <resource> and gpt-4.1 with your Azure OpenAI resource name and deployment name.
var agent = new OpenAIClient(
    new BearerTokenPolicy(new AzureCliCredential(), "https://ai.azure.com/.default"),
    new OpenAIClientOptions() { Endpoint = new Uri("https://<resource>.openai.azure.com/openai/v1") })
    .GetResponsesClient("gpt-4.1")
    .AsAIAgent(name: "HaikuBot", instructions: "You are an upbeat assistant that writes beautifully.");

Console.WriteLine(await agent.RunAsync("Write a haiku about Microsoft Agent Framework."));

That’s it — a working AI agent in a handful of lines. From here you can add function tools, sessions for multi-turn conversations, streaming responses, and more.

Multi-Agent Workflows

Single agents are powerful, but real-world applications often need multiple agents working together. Agent Framework ships with a workflow engine that lets you compose agents into orchestration patterns — sequential, concurrent, handoff, and group chat — all with streaming support built in.

Here’s a sequential workflow where a copywriter agent drafts a tagline and a reviewer agent provides feedback:

Python

pip install agent-framework-orchestrations --pre
import asyncio
from typing import cast

from agent_framework import Message
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import SequentialBuilder
from azure.identity import AzureCliCredential


async def main() -> None:
    client = AzureOpenAIChatClient(credential=AzureCliCredential())

    writer = client.as_agent(
        instructions="You are a concise copywriter. Provide a single, punchy marketing sentence based on the prompt.",
        name="writer",
    )

    reviewer = client.as_agent(
        instructions="You are a thoughtful reviewer. Give brief feedback on the previous assistant message.",
        name="reviewer",
    )

    # Build sequential workflow: writer -> reviewer
    workflow = SequentialBuilder(participants=[writer, reviewer]).build()

    # Run and collect outputs
    outputs: list[list[Message]] = []
    async for event in workflow.run("Write a tagline for a budget-friendly eBike.", stream=True):
        if event.type == "output":
            outputs.append(cast(list[Message], event.data))

    if outputs:
        for msg in outputs[-1]:
            name = msg.author_name or "user"
            print(f"[{name}]: {msg.text}")


if __name__ == "__main__":
    asyncio.run(main())

.NET

dotnet add package Microsoft.Agents.AI.Workflows --prerelease
using System.ClientModel.Primitives;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
using OpenAI;

// Replace <resource> and gpt-4.1 with your Azure OpenAI resource name and deployment name.
var chatClient = new OpenAIClient(
    new BearerTokenPolicy(new AzureCliCredential(), "https://ai.azure.com/.default"),
    new OpenAIClientOptions() { Endpoint = new Uri("https://<resource>.openai.azure.com/openai/v1") })
    .GetChatClient("gpt-4.1")
    .AsIChatClient();

ChatClientAgent writer = new(chatClient,
    "You are a concise copywriter. Provide a single, punchy marketing sentence based on the prompt.",
    "writer");

ChatClientAgent reviewer = new(chatClient,
    "You are a thoughtful reviewer. Give brief feedback on the previous assistant message.",
    "reviewer");

// Build sequential workflow: writer -> reviewer
Workflow workflow = AgentWorkflowBuilder.BuildSequential(writer, reviewer);

List<ChatMessage> messages = [new(ChatRole.User, "Write a tagline for a budget-friendly eBike.")];

await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, messages);

await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
{
    if (evt is AgentResponseUpdateEvent e)
    {
        Console.Write(e.Update.Text);
    }
}

Migration from Semantic Kernel and AutoGen

If you’ve been building agents with Semantic Kernel or AutoGen, Agent Framework is the natural next step. We’ve published detailed migration guides to help you transition:

What’s Next?

This Release Candidate represents an important step toward General Availability. We encourage you to try the framework and share your feedback — your input is invaluable as we finalize the release in the coming weeks. Reach out to us on GitHub or on Discord.

For more information, check out our documentation and examples on GitHub, and install the latest packages from NuGet (.NET) or PyPI (Python).

The post Microsoft Agent Framework Reaches Release Candidate appeared first on Microsoft Foundry Blog.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Gas Town Explained: How to Use Goosetown for Parallel Agentic Engineering

1 Share

Goosetown

On New Year's Day 2026, while many were recovering from the night before, a different kind of hangover took hold of every AI-pilled, chronically online software engineer. Steve Yegge published a new blog post: "Welcome to Gas Town." Some walked away inspired to finally use their agents optimally; others were just plain confused. If you're like me, you felt a bit of both.

Yegge's 34 minute post is a sprawling vision filled with futuristic ideas, playful characters, and enough side tangents to make your head spin. But underneath the lore is a massive architectural shift. I want to take a step back and simplify the "Big Idea" for everyone: Gas Town is a philosophy and a proof of concept to help people coordinate multiple agents working together.

The Paradigm Shift of Agentic Engineering

Most people use AI agents sequentially. The workflow can look like this:

  • 1:00 PM: You: "goose, build the API endpoint."
  • [Wait 10 minutes, check back.]
  • 1:10 PM: You: "Now build the frontend."
  • [Wait 10 minutes, check back.]
  • 1:20 PM: You: "Now write the tests."
  • [Wait 10 minutes, check back.]
  • 1:30 PM: Project complete.

You've built a project in 30 minutes, which is fast, but you spent most of that time just watching a progress bar. Some engineers started to realize that if we are running one agent, we can run another five at the same time.

For example, Agent A builds the API, Agent B can start the frontend, Agent C can write tests, and Agent D can investigate a bug in that legacy codebase you've been avoiding.

This is how people are buying their time back. They're getting entire sprints done in an hour by running parallel threads. (Just don't tell your boss because the reward for finishing work is always more work.)

However, since agents don't communicate with each other, this approach introduces new problems:

  • Merge conflicts: Two agents change the same line in the same file and break everything.
  • Lost context: Sessions crash or the agent starts hallucinating because it's been talking too long, and suddenly an hour of "work" vanishes.
  • Human bottleneck: You end up constantly checking your phone at a party on the weekend or in bed to see if your agents are still on track making you a babysitter for agents.

Gas Town Explained

Gas Town is designed to stop the babysitting. It coordinates the distribution of tasks among parallel agents so you don't have to. The system uses:

  • Worktrees: These automatically put each agent in its own separate workspace so they don't step on each other's toes.
  • Beads: It uses beads to track progress. If a session crashes, the next agent session can pick up exactly where the last agent left off.
  • Communication: Each agent reports aloud what it's up to or observing, so other agents gain the necessary context.

This system also introduces a cast of characters:

  • The Mayor: your main agent interface that coordinates all the other agents
  • The Polecat(s): These are worker agents. They work on separate work trees, and take instruction from the Mayor.
  • The Witness: Observes the worker agents, nudges them when they get stuck, and escalates issues to keep the system running

I won't list every single character here (it gets deep), but the takeaway is: Gas Town creates a chain of command with a shared way to communicate.

Introducing Goosetown

This is exactly the kind of futuristic thinking we're building toward at goose. So the goose team, specifically Tyler Longwell, built our own take on this called Goosetown.

Goosetown is a multi-agent orchestration layer built on top of goose. Like Gas Town, it coordinates parallel agents. Unlike Gas Town, it's deliberately minimal and built for research-first parallel work.

When you give Goosetown a task, the main agent acts as an Orchestrator, breaking the job into phases: research, build, and review. Then, it spawns parallel delegates to get it done. Each delegate communicates via a shared Town Wall, an append-only log where every agent posts what they're doing and what they've found.

Here's a real Town Wall snippet from a session where parallel researchers converged on a pivot quickly:

  • [10:14] researcher-api-auth - 🚨 POTENTIAL SHOWSTOPPER: Service callers have EMPTY capabilities. Planned auth path will silently reject every request. This needs a code change, not just config.
  • [10:14] researcher-endpoints - 💡 Found: native endpoint already exists with minimal deps. Alternative path viable.
  • [10:15] researcher-source - ✅ Done. Confirmed: native path requires zero new dependencies. Recommending pivot.

Goosetown operates on 4 components: skills, subagents, beads, and a gtwall.

Skills

Skills are Markdown files that describe how to do something like "how to deploy to production." Goosetown uses these to tell each Delegate how to do its specific job. When a Delegate spawns, it's "pre-loaded" with the skill for its role (Orchestrator, Researcher, Writer, Reviewer).

Subagents

Instead of doing everything in one long conversation that eventually hits a "context cliff," Goosetown uses subagents, ephemeral agent instances. These are triggered by the summon extension, using delegate() to hand off work to a fresh agent instance. They do the work in their own clean context and return a summary, keeping your main session fast and focused.

Beads

Goosetown uses Beads to track progress so work survives crashes. It's a local issue tracker based on Git. The Orchestrator creates issues, delegates update them, and if a session fails, the next agent picks up the "bead" and continues the work.

gtwall

gtwall is an append-only log that delegates use to communicate and coordinate. All delegates post and read activity.

A Note from the Creator

I mostly use Goosetown when I'm trying to answer something that has a lot of independent angles, where missing a constraint is more expensive than spending extra tokens. For example, integration research ('how does system X actually authenticate?') or migration planning ('what would break first if we moved?')

There's a real tax to running multiple agents. If I can describe the task in one paragraph and I already know where to start, I don't need Goosetown. Parallelism improves throughput, but it adds coordination overhead.

The next improvements I care about are mostly about making failure modes cheaper. The system already has turn limits and a flat hierarchy, but it doesn't yet have good cost controls beyond that. Token budgets and basic circuit breakers would make it harder for a delegate to burn a surprising amount of compute in a tight loop.

On the coordination side, I'm interested in adding a little more structure without turning it into a framework. Even lightweight conventions, like consistent prefixes on wall messages or a clearer artifact layout, can reduce synthesis work.

Longer term, goose has roadmap work around more structured agent-to-agent communication. If that lands, it might replace parts of the wall. The tradeoff is the one we've been making all along: structure buys you scale and tooling; simplicity buys you debuggability and the ability to change policy by editing a markdown file.

— Tyler Longwell

Get Started

Ready to try parallel agentic engineering for yourself? Goosetown is open source and available on GitHub. Clone the repo, follow the setup instructions in the README, and you'll be orchestrating multiple agents in no time. If you're new to this workflow, watching the video below is a great way to see what a real session looks like before diving in.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories