Blog
ENGINEERING

What to consider when standardizing your REST APIs

Seth Carney

October 06, 2020

API Post Header

Table of contents

Standardization helps your users avoid bugs and code debt

What elements should you standardize?

Standardizing your HTTP response status codes

Other ways to make your REST API more developer-friendly

Recently, we’ve been working on standardizing some aspects of Courier’s REST APIs, such as naming conventions and HTTP status response codes. While Courier has an extensive web UI where our Design Studio lives, we have multiple APIs behind the scenes that do a lot of the heavy lifting. It’s important to make sure these APIs are as developer-friendly as possible, meaning they should be semantically consistent and predictable. I thought I’d share some of our guiding principles and learnings that might be useful for your team as well.

Standardization helps your users avoid bugs and code debt

As with most things in life, when it comes to writing a REST API, consistency is key. A REST API is a communication layer, and like any language, it has certain rules and conventions that should be followed in order for things to run smoothly. The different methods (GET, POST, DELETE, etc.) have specific behaviors that users will expect the API to follow. HTTP response codes are standardized as well so that users know what the problem was and can take some action depending on the response.

Deviations from the norm or inconsistencies within the API itself means there most likely will be differences between what the user expects to happen, and what the API’s actual behavior looks like. As a developer, you’ve probably experienced this (we definitely have), where the way that you call one part of the API looks completely different from the way you make a nearly identical call in another part of the API. In the worst case, this means your users end up with buggy code, and in the best case, it means they are going to have to branch their code to handle any special cases, making it harder for them to maintain the integration. Plus, any nonstandard aspects of your API will also make it harder for your own team to manage and update it.

What elements should you standardize?

  • Your HTTP methods. While there might be different philosophies here, we recommend using the most specific method that fits. For example, using a generic POST for updating resources doesn’t tell the user anything about the update behavior. Instead, you might want to support PUT and PATCH methods where appropriate, so that users have more information about the type of update they’re making. A PUT completely overwrites the resource if it already exists, while a PATCH is meant for a partial update.
  • Your variable names. We’ve recently updated our variable names on our inbound requests so that everything is “snake case” (meaning words are separated by an underscore , like first_name) rather than camel case (firstName). This is important because case-sensitivity and URLs don’t generally play well together!
  • Your responses. We’ll talk later about why it’s important to return objects in your responses in order to future-proof your API.
  • Your HTTP response status codes. This tells your user what happened as a result of their API call, so it’s important that these codes are used consistently. We’ll go through these in more detail in a moment.
  • Your error reporting. It’s useful to create a standard interface for reporting errors that users can always expect from your API, and this helps you extend your API as well if you need to add more types of errors.

Standardizing your HTTP response status codes

When an API call is made, the server returns a three-digit HTTP status code in its response. These codes start in the 100s and go to the 500s. A lot of the work we’ve done recently has been to standardize these responses so that they fully describe Courier’s behavior in every case.

Success conditions

Codes in the 200s indicate that the client’s request was successful. Within this category, there are several possible statuses. The ones we care about at Courier are 200, 202 and 204.

A 200 response code means that you were successful, *and *you can expect more information in the response body (such as the data that you requested with a GET). To create a more developer-friendly API, we’ve been moving some of these generic 200 responses to more specific 202 or 204 responses. A 202 means that Courier has accepted the request, but hasn’t been able to process it yet. This is mostly used for our Send pipelines and tracking. And a 204 means that the request was successful, but there’s no data to return back in the body. This might happen for example if you DELETE a resource; beyond acknowledging that this was successful, there’s nothing else that the user expects to receive in that moment from Courier.

A note about status code 201: officially this code means that a resource was created, but we’ve found that in practice most APIs just return a 200 for this, since the fact that you’re using the API with an action like POST means that your intention is to create a resource.

Error conditions

As we get into error conditions, response codes become even more useful. 4xx codes describe various types of client errors that can happen (for Courier, usually this is a data validation error), while 5xx codes are for server-side errors (something happened with Courier’s backend that caused the request to fail).

One thing we’ve found useful is to develop a consistent and standard error interface, so that when you’re integrating with Courier, you can always expect that same structure for an error response. This looks like:

Copied!

{ 
code?: string; // a string indicating the error code
doc_url?: string; // an optional link to documentation
message?: string; // a human-readable error message
type: // the type of error returned
   | “api_error”
   | “authentication_error”
   | “authorization_error”
   |
}

By defining an opinionated error response type it allows Courier to convey further information to the developer which could be used to fix and resubmit the request.

Other ways to make your REST API more developer-friendly

Use Nesting

If you want to make your API easier to expand in the future, it’s important to accept and return objects that encapsulate the raw data. Even if you’re just returning something like a message ID, returning an object will help you make sure that additions to the response later on don’t break anything for your legacy users.

For example, instead of returning a message tracking ID  like this:

“4bccd5bf-3bdc-49f4-b2d7-7851338bd105”

With objects at the root level, your API would look more like this:

Copied!

{
messageId “4bccd5bf-3bdc-49f4-b2d7-7851338bd105”
}

Make your API idempotent

When you API is idempotent, this means that retries won’t affect the results in unexpected ways. For example, if you request to send an email through an API, and don’t receive a response for some reason (a network error, for example), you might not know what to do. Should you resend the request and risk sending duplicate emails? If the API is idempotent, you don’t have to worry, because a retry will not result in duplication.

At Courier, we support idempotency on send requests through an Idempotency-Key header, which signals to Courier that multiples of the same request should be treated as idempotent

I hope this article has been helpful.

Similar resources

Building Notification Infrastructure with Claude Code and Cursor
GuideEngineering

Terminal-First Development vs. IDE: Building Notification Infrastructure with Claude Code and Cursor

AI coding tools split into two camps: terminal agents (Claude Code) and IDE-augmented editors (Cursor). This guide compares both approaches using Courier's CLI and MCP server as the test case. Covers installation, configuration, and practical workflows for building multi-channel notifications. Includes code examples for user management, bulk operations, and automation triggers. Also explores agent-to-agent communication patterns where AI systems need notification infrastructure to coordinate tasks and escalate to humans.

By Kyle Seyler

January 29, 2026

notification platform for developers
EngineeringNotifications Landscape

The Notification Platform Developers Choose

Most notification platforms built dashboards first and added developer tools later. Courier did the opposite. With a CLI that handles real workflows, MCP integration with setup management, typed SDKs in seven languages, and SOC 2 Type 2 certification, Courier is built for teams that ship. This isn't marketing copy: Twilio chose Courier to unify notifications across their 10M+ developer platform. LaunchDarkly uses Courier to power feature release workflows. When the companies that build developer infrastructure choose your notification platform, that says something about the technical foundation.

By Kyle Seyler

January 26, 2026

a guide for ai assisted development: Notification infrastructure
CourierNotifications LandscapeEngineering

Vibe Coding Notifications: How to Use Courier with Cursor or Claude Code

Courier's MCP server lets AI coding tools like Cursor and Claude Code interact directly with your notification infrastructure. Unlike Knock and Novu's MCP servers that focus on API operations, Courier's includes embedded installation guides for Node, Python, Flutter, React, and other platforms. When you prompt "add Courier to my app," your AI assistant pulls accurate setup instructions rather than relying on outdated training data. OneSignal's MCP is community-maintained, not official. Courier supports 50+ providers, native Slack/Teams integration, drop-in inbox and preference components, and a free tier of 10,000 notifications/month. Configure in Cursor with "url": "https://mcp.courier.com" and "headers": { "api_key": "YOUR_KEY" }.

By Kyle Seyler

January 22, 2026

Multichannel Notifications Platform for SaaS

Products

Platform

Integrations

Customers

Blog

API Status

Subprocessors


© 2026 Courier. All rights reserved.