diff --git a/web/docs/advanced/apis.md b/web/docs/advanced/apis.md
index 0399f9a8d7..be42ecc083 100644
--- a/web/docs/advanced/apis.md
+++ b/web/docs/advanced/apis.md
@@ -1,3 +1,224 @@
---
title: Custom HTTP API Endpoints
---
+import { ShowForTs, ShowForJs } from '@site/src/components/TsJsHelpers';
+
+In Wasp, the default client-server interaction mechanism is through [Operations](/docs/database/operations). However, if you need a specific URL method/path, or a specific response, Operations may not be suitable for you. For these cases, you can use an `api`! Best of all, they should look and feel very familiar.
+
+## How to create an API
+
+APIs are used to tie a JS function to an certain endpoint e.g. `POST /something/special`. They are distinct from Operations and have no client-side helpers (like `useQuery`).
+
+To create a Wasp API, you must:
+1. Declare the API in Wasp using the `api` declaration
+2. Define the API's NodeJS implementation
+
+After completing these two steps, you'll be able to call the API from the client code (via our `Axios` wrapper), or from the outside world.
+
+### Declaring the API in Wasp
+First we need to declare the API in the Wasp file and you can easily do this with the `api` declaration:
+
+```wasp title="main.wasp"
+// ...
+
+api fooBar { // APIs and their implementations don't need to (but can) have the same name.
+ fn: import { fooBar } from "@server/apis.js",
+ httpRoute: (GET, "/foo/bar")
+}
+```
+
+The `api` declaration supports the following fields:
+- `fn: ServerImport` (required) - The import statement of the APIs NodeJs implementation.
+- `httpRoute: (HttpMethod, string)` (required) - The HTTP (method, path) pair, where the method can be one of:
+ - `ALL`, `GET`, `POST`, `PUT` or `DELETE`
+ - and path is an Express path `string`.
+- `entities: [Entity]` (optional) - A list of entities you wish to use inside your API.
+We'll leave this option aside for now. You can read more about it [here](#using-entities-in-apis).
+- `auth: bool` (optional) - If auth is enabled, this will default to `true` and provide a `context.user` object. If you do not wish to attempt to parse the JWT in the Authorization Header, you may set this to `false`.
+- `middlewareConfigFn: ServerImport` (optional) - The import statement to an Express middleware config function for this API. See [the guide here](/docs/advanced/middleware-config).
+
+### Defining the APIs NodeJS implementation
+
+
+
+:::note
+To make sure the Wasp compiler generates the types for APIs for use in the NodeJS implementation, you should add your `api` declarations to your `.wasp` file first _and_ keep the `wasp start` command running.
+:::
+
+
+After you defined the API, it should be implemented as a NodeJS function that takes three arguments:
+1. `req`: Express Request object
+2. `res`: Express Response object
+3. `context`: An additional context object **injected into the API by Wasp**. This object contains user session information, as well as information about entities. The examples here won't use the context for simplicity purposes. You can read more about it in the [section about using entities in APIs](#using-entities-in-apis).
+
+
+
+
+```ts title="src/server/apis.js"
+export const fooBar = (req, res, context) => {
+ res.set('Access-Control-Allow-Origin', '*') // Example of modifying headers to override Wasp default CORS middleware.
+ res.json({ msg: `Hello, ${context.user?.username || "stranger"}!` })
+}
+```
+
+
+
+```ts title="src/server/apis.ts"
+import { FooBar } from '@wasp/apis/types' // This type is generated by Wasp based on the `api` declaration above.
+
+export const fooBar: FooBar = (req, res, context) => {
+ res.set('Access-Control-Allow-Origin', '*') // Example of modifying headers to override Wasp default CORS middleware.
+ res.json({ msg: `Hello, ${context.user?.username || "stranger"}!` })
+}
+```
+
+
+
+
+
+#### Providing extra type information
+
+We'll see how we can provide extra type information to an API function.
+
+Let's say you wanted to create some `GET` route that would take an email address as a param, and provide them the answer to "Life, the Universe and Everything." :) What would this look like in TypeScript?
+
+Define the API in Wasp:
+
+```wasp title="main.wasp"
+api fooBar {
+ fn: import { fooBar } from "@server/apis.js",
+ entities: [Task],
+ httpRoute: (GET, "/foo/bar/:email")
+}
+```
+
+We can use the `FooBar` type to which we'll provide the generic **params** and **response** types, which then gives us full type safety in the implementation.
+
+```ts title="src/server/apis.ts"
+import { FooBar } from '@wasp/apis/types'
+
+export const fooBar: FooBar<
+{ email: string }, // params
+{ answer: number } // response
+> = (req, res, _context) => {
+ console.log(req.params.email)
+ res.json({ answer: 42 })
+}
+```
+
+
+## Using the API
+
+### Using the API externally
+
+To use the API externally, you simply call the endpoint using the method and path you used.
+
+For example, if your app is running at `https://example.com` then from the above you could issue a `GET` to `https://example/com/foo/callback` (in your browser, Postman, `curl`, another web service, etc.).
+
+### Using the API from the client
+
+To use the API from your client, including with auth support, you can import the Axios wrapper from `@wasp/api` and invoke a call. For example:
+```ts
+import React, { useEffect } from 'react'
+import api from '@wasp/api'
+
+async function fetchCustomRoute() {
+ const res = await api.get('/foo/bar')
+ console.log(res.data)
+}
+
+export const Foo = () => {
+ useEffect(() => {
+ fetchCustomRoute()
+ }, []);
+
+ return (
+ <>
+ // ...
+ >
+ )
+}
+```
+
+#### Making sure CORS works
+APIs are designed to be as flexible as possible, hence they don't utilize the default middleware like Operations do. As a result, to use these APIs on the client side, you must ensure that CORS (Cross-Origin Resource Sharing) is enabled.
+
+You can do this by defining custom middleware for your APIs in the Wasp file.
+
+
+
+
+For example, an `apiNamespace` is a simple declaration used to apply some `middlewareConfigFn` to all APIs under some specific path:
+```wasp title="main.wasp"
+apiNamespace fooBar {
+ middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js",
+ path: "/foo"
+}
+```
+And then in the implementation file:
+```js title="src/server/apis.js"
+export const apiMiddleware = (config) => {
+ return config;
+}
+```
+
+
+
+For example, an `apiNamespace` is a simple declaration used to apply some `middlewareConfigFn` to all APIs under some specific path:
+```wasp title="main.wasp"
+apiNamespace fooBar {
+ middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js",
+ path: "/foo"
+}
+```
+And then in the implementation file (returning the default config):
+```ts title="src/server/apis.ts"
+import { MiddlewareConfigFn } from "@wasp/middleware"
+export const apiMiddleware: MiddlewareConfigFn = (config) => {
+ return config;
+}
+```
+
+
+
+We are returning the default middleware which enables CORS for all APIs under the `/foo` path.
+
+For more information about middleware configuration, please see: [Middleware Configuration](/docs/advanced/middleware-config)
+
+
+## Using Entities in APIs
+In many cases, resources used in APIs will be [Entities](#entity).
+To use an Entity in your API, add it to the `api` declaration in Wasp:
+
+```wasp {3} title="main.wasp"
+api fooBar {
+ fn: import { fooBar } from "@server/apis.js",
+ entities: [Task],
+ httpRoute: (GET, "/foo/bar")
+}
+```
+
+Wasp will inject the specified Entity into the APIs `context` argument, giving you access to the Entity's Prisma API:
+
+
+
+
+```ts title="src/server/apis.js"
+export const fooBar = (req, res, context) => {
+ res.json({ count: await context.entities.Task.count() })
+}
+```
+
+
+
+```ts title="src/server/apis.ts"
+import { FooBar } from '@wasp/apis/types'
+
+export const fooBar: FooBar = (req, res, context) => {
+ res.json({ count: await context.entities.Task.count() })
+}
+```
+
+
+
+The object `context.entities.Task` exposes `prisma.task` from [Prisma's CRUD API](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/crud).