> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.icepanel.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.icepanel.io/_mcp/server.

# Invite users via API

> Programmatically invite teammates to your IcePanel organization.

This guide shows how to invite users to your organization using the API. This is useful for automating onboarding workflows or granting temporary access to external collaborators.

> **Note**
>
> Prerequisites:
>
> * IcePanel account
> * API key (created from `https://app.icepanel.io/organizations/:organizationId/manage/api-keys`)

```bash
export ICEPANEL_API_KEY='your-api-key'
export ICEPANEL_ORGANIZATION_ID='your-organization-id'
```

## Steps

#### List current invites

Before creating a new invite, check whether the user already has a pending one:

### Request

GET [https://api.icepanel.io/v1/organizations/\{organizationId}/users/invites](https://api.icepanel.io/v1/organizations/\{organizationId}/users/invites)

```curl
curl https://api.icepanel.io/v1/organizations/organizationId/users/invites \
     -H "X-API-Key: <apiKey>"
```

```typescript
import { IcePanelClient } from "@icepanel/sdk";

async function main() {
    const client = new IcePanelClient({
        apiKey: "YOUR_API_KEY_HERE",
    });
    await client.organizations.users.invites.list({
        organizationId: "organizationId",
    });
}
main();

```

```java
package com.example.usage;

import com.icepanel.IcePanelClient;
import com.icepanel.organizations.users.types.OrganizationUserInvitesListRequest;

public class Example {
    public static void main(String[] args) {
        IcePanelClient client = IcePanelClient
            .builder()
            .apiKey("YOUR_API_KEY_HERE")
            .build();

        client.organizations().users().invites().list(
            OrganizationUserInvitesListRequest
                .builder()
                .organizationId("organizationId")
                .build()
        );
    }
}
```

```python
import requests

url = "https://api.icepanel.io/v1/organizations/organizationId/users/invites"

headers = {"X-API-Key": "<apiKey>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```go
package main

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

func main() {

	url := "https://api.icepanel.io/v1/organizations/organizationId/users/invites"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("X-API-Key", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```csharp
using RestSharp;

var client = new RestClient("https://api.icepanel.io/v1/organizations/organizationId/users/invites");
var request = new RestRequest(Method.GET);
request.AddHeader("X-API-Key", "<apiKey>");
IRestResponse response = client.Execute(request);
```

This endpoint returns all invites, including used and revoked ones. An invite is still active if it has no `usedAt` or `revokedAt` field and its `expiresAt` is in the future.

#### Create an invite

Send an invite to a user by email. Set `permission` to control their access level (`read`, `write`, `admin`, or `billing`), and `expiresAt` to define how long the invite remains valid.

### Request

POST [https://api.icepanel.io/v1/organizations/\{organizationId}/users/invites](https://api.icepanel.io/v1/organizations/\{organizationId}/users/invites)

```curl
curl -X POST https://api.icepanel.io/v1/organizations/organizationId/users/invites \
     -H "X-API-Key: <apiKey>" \
     -H "Content-Type: application/json" \
     -d '{
  "email": "string",
  "expiresAt": "2024-01-15T09:30:00Z",
  "permission": "billing"
}'
```

```typescript
import { IcePanelClient } from "@icepanel/sdk";

async function main() {
    const client = new IcePanelClient({
        apiKey: "YOUR_API_KEY_HERE",
    });
    await client.organizations.users.invites.create({
        organizationId: "organizationId",
        body: {
            email: "string",
            expiresAt: new Date("2024-01-15T09:30:00Z"),
            permission: "billing",
        },
    });
}
main();

```

```java
package com.example.usage;

import com.icepanel.IcePanelClient;
import com.icepanel.organizations.users.types.OrganizationUserInviteCreateRequest;
import com.icepanel.types.OrganizationUserInviteRequired;
import com.icepanel.types.PermissionType;
import java.time.OffsetDateTime;

public class Example {
    public static void main(String[] args) {
        IcePanelClient client = IcePanelClient
            .builder()
            .apiKey("YOUR_API_KEY_HERE")
            .build();

        client.organizations().users().invites().create(
            OrganizationUserInviteCreateRequest
                .builder()
                .organizationId("organizationId")
                .body(
                    OrganizationUserInviteRequired
                        .builder()
                        .email("string")
                        .expiresAt(OffsetDateTime.parse("2024-01-15T09:30:00Z"))
                        .permission(PermissionType.BILLING)
                        .build()
                )
                .build()
        );
    }
}
```

```python
import requests

url = "https://api.icepanel.io/v1/organizations/organizationId/users/invites"

payload = {
    "email": "string",
    "expiresAt": "2024-01-15T09:30:00Z",
    "permission": "billing"
}
headers = {
    "X-API-Key": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```go
package main

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

func main() {

	url := "https://api.icepanel.io/v1/organizations/organizationId/users/invites"

	payload := strings.NewReader("{\n  \"email\": \"string\",\n  \"expiresAt\": \"2024-01-15T09:30:00Z\",\n  \"permission\": \"billing\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("X-API-Key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```csharp
using RestSharp;

var client = new RestClient("https://api.icepanel.io/v1/organizations/organizationId/users/invites");
var request = new RestRequest(Method.POST);
request.AddHeader("X-API-Key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"email\": \"string\",\n  \"expiresAt\": \"2024-01-15T09:30:00Z\",\n  \"permission\": \"billing\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

> **Warning**
>
> Note that invites with `write` or `admin` permission count against your paid seat limit. If you are at capacity, creating one returns a **409** error with `"All organization seats have been assigned"`.
> Invites with `read` permission do not count against this limit. [See pricing](https://icepanel.io/pricing)

Note that the `createdBy` field in the response will be `"api-key"` when created via API key, **not** a user ID. This is useful for auditing who or what triggered the invite.

The invited user will receive an email like this:

![IcePanel invite email](/_fern-img/3c3e5988d3bb93182b3fe7a00c85de564862efa4cdf1cd399f119413134b114c.webp)

Note the invite `id` in the response if you want to revoke the invite in the next step.

#### Revoke an invite (optional)

If you need to cancel an invite before it is accepted, revoke it using the invite ID:

### Request

POST [https://api.icepanel.io/v1/organizations/\{organizationId}/users/invites/\{organizationUserInviteId}](https://api.icepanel.io/v1/organizations/\{organizationId}/users/invites/\{organizationUserInviteId})

```curl
curl -X POST https://api.icepanel.io/v1/organizations/organizationId/users/invites/organizationUserInviteId \
     -H "X-API-Key: <apiKey>"
```

```typescript
import { IcePanelClient } from "@icepanel/sdk";

async function main() {
    const client = new IcePanelClient({
        apiKey: "YOUR_API_KEY_HERE",
    });
    await client.organizations.users.invites.revoke({
        organizationId: "organizationId",
        organizationUserInviteId: "organizationUserInviteId",
    });
}
main();

```

```java
package com.example.usage;

import com.icepanel.IcePanelClient;
import com.icepanel.organizations.users.types.OrganizationUserInviteRevokeRequest;

public class Example {
    public static void main(String[] args) {
        IcePanelClient client = IcePanelClient
            .builder()
            .apiKey("YOUR_API_KEY_HERE")
            .build();

        client.organizations().users().invites().revoke(
            OrganizationUserInviteRevokeRequest
                .builder()
                .organizationId("organizationId")
                .organizationUserInviteId("organizationUserInviteId")
                .build()
        );
    }
}
```

```python
import requests

url = "https://api.icepanel.io/v1/organizations/organizationId/users/invites/organizationUserInviteId"

headers = {"X-API-Key": "<apiKey>"}

response = requests.post(url, headers=headers)

print(response.json())
```

```go
package main

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

func main() {

	url := "https://api.icepanel.io/v1/organizations/organizationId/users/invites/organizationUserInviteId"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("X-API-Key", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```csharp
using RestSharp;

var client = new RestClient("https://api.icepanel.io/v1/organizations/organizationId/users/invites/organizationUserInviteId");
var request = new RestRequest(Method.POST);
request.AddHeader("X-API-Key", "<apiKey>");
IRestResponse response = client.Execute(request);
```

The response returns the updated invite object with `revokedAt` and `revokedBy` fields populated.

> **Note**
>
> This endpoint is idempotent. Revoking an already-revoked invite will not return an error. Revoked invites remain in the list returned by `GET /users/invites` and are not deleted.