# Create an invitation — API reference

> Creates an invitation for an email address with optional language, role and extra team.

Source: https://upfour.io/docs/api/invites/create-an-invitation

[Developers](https://upfour.io/docs/api.md)/[Invites](https://upfour.io/docs/api/invites.md)

Creates an invitation for an email address with optional language, role and extra team.

POST https://app.upfour.io/v1/invites

scope member.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201

## Body

`application/json` required 4 fields

emailstring (email) (required)

In the app: **Email address**

All other fields3 fields

languagestring · nullable

Language of the invitation mail; omitted lets up4 pick from the invited person, the company and the inviter.

ende

rolestring

The company role the person gets on accepting. Omitted or any other value invites as member.

owneradminmemberviewer

In the app: **Role**

team_iduuid · nullable

An extra team to add the person to besides the default team.

In the app: **Team**

## Returns

A successful request returns `201` with application/json.

Response fields 1 top-level fields

| Field | Type | Description |
| --- | --- | --- |
| `data` | Invite | |
| Invite · 16 fields FieldTypeDescription `created_at` string (date-time) `email` string `expired` boolean Always false in these answers; compare expires_at with the current time. `expires_at` string (date-time) `id` uuid `invited_by` uuid `organization_id` uuid `role` string one of`owner` `admin` `member` `viewer` `editor` The company role; for a client portal invite (client_id set) the client member role viewer, editor or admin. `accepted_at` string (date-time) `client_id` uuid Set for a client portal invite, whose person only sees that client. `client_name` string `inviter_email` string `language` string one of`en` `de` `org_name` string `team_id` uuid `team_name` string | | |

## Errors

Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md).

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. |
| 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. |
| 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. |
| 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. |
| 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. |
| 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | The protection limit for this action is used up; retry after the response's `Retry-After` delay. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached`: the protection limit of this action is used up for now. `error.details` carries `purpose`, `limit`, `window` and `retry_after_seconds`; the `Retry-After` header says when the same request can succeed. |

Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md).

[PreviousList invitations](https://upfour.io/docs/api/invites/list-invitations.md) [NextResend an invitation](https://upfour.io/docs/api/invites/resend-an-invitation.md)

## Request

### cURL

```bash
curl -s -X POST "https://app.upfour.io/v1/invites" \
  -H "Authorization: Bearer $UP4_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "dan@example.com",
    "language": "en",
    "role": "member"
  }'
```

### JavaScript

```javascript
const res = await fetch("https://app.upfour.io/v1/invites", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UP4_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "dan@example.com",
    language: "en",
    role: "member",
  }),
});
console.log(res.status, await res.json());
```

### Python

```python
import os
import requests

res = requests.post(
    "https://app.upfour.io/v1/invites",
    headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"},
    json={
        "email": "dan@example.com",
        "language": "en",
        "role": "member",
    },
)
print(res.status_code, res.json())
```

### Go

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	payload := strings.NewReader(`{
  "email": "dan@example.com",
  "language": "en",
  "role": "member"
}`)
	req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/invites", payload)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.StatusCode, string(out))
}
```

### PHP

```php
<?php
$payload = <<<'JSON'
{
  "email": "dan@example.com",
  "language": "en",
  "role": "member"
}
JSON;

$ch = curl_init("https://app.upfour.io/v1/invites");
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("UP4_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => $payload,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n";
curl_close($ch);
```

## Response

### 201

```json
{
  "data": {
    "created_at": "2026-10-27T09:00:00Z",
    "email": "dan@example.com",
    "expired": false,
    "expires_at": "2026-11-03T09:00:00Z",
    "id": "00000000-0000-4000-8000-000000000001",
    "invited_by": "00000000-0000-4000-8000-000000000002",
    "language": "en",
    "organization_id": "00000000-0000-4000-8000-000000000003",
    "role": "member"
  }
}
```

### 400

```json
{
  "error": {
    "code": "bad_request",
    "message": "The request is malformed or fails validation.",
    "request_id": "req_…"
  }
}
```

### 402

```json
{
  "error": {
    "code": "billing_action_unavailable",
    "message": "The Company's billing state does not allow this action.",
    "request_id": "req_…"
  }
}
```

### 403

```json
{
  "error": {
    "code": "permission_denied",
    "message": "The credentials do not permit this action.",
    "request_id": "req_…"
  }
}
```

### 409

```json
{
  "error": {
    "code": "conflict",
    "message": "The request conflicts with the current state.",
    "request_id": "req_…"
  }
}
```

### 429

```json
{
  "error": {
    "code": "quota_limit_reached",
    "message": "The protection limit for this action is used up; retry after the response's Retry-After delay.",
    "request_id": "req_…"
  }
}
```

### 503

```json
{
  "error": {
    "code": "quota_gate_unavailable",
    "message": "The limit check itself failed, so the action was not carried out.",
    "request_id": "req_…"
  }
}
```
