The Canva Admin API is currently provided as a preview and is subject to change.

Create user

Creates a managed user in the caller's organization. The email domain must be a verified domain owned by the caller's organization, and the SAML identity provider (resolved by idp_issuer) must belong to that organization. If the email already belongs to an existing Canva account that isn't part of an organization, that account is brought into the organization rather than a new one being created; the person may be asked to confirm. If successful, the user's Canva ID, email, display name, first name, last name, and organization role are returned.

HTTP method and URL path

POST https://api.canva.com/admin/v1/users

Authentication and authorization

This endpoint requires a valid access token that was generated using client credentials.

Scopes

The access token must have all the following scopes (permissions):

  • admin:user:write
  • admin:team:write

Header parameters

Authorizationstring
Required

Provides credentials to authenticate the request, in the form of a Bearer token.

For example: Authorization: Bearer {token}

Content-Typestring
Required

Indicates the media type of the information sent in the request. This must be set to application/json.

For example: Content-Type: application/json

Body parameters

emailstring
Required

The email address of the user. The email domain must be a verified domain owned by the caller's organization.

Minimum length: 1

Maximum length: 254

display_namestring
Required

The display name of the user.

Minimum length: 1

saml_accountSamlAccount
Required

The SAML account details used to link the user to the organization's identity provider for SSO.

idp_issuerstring
Required

The issuer URL of the SAML identity provider. Must resolve to an identity provider owned by the caller's organization.

Minimum length: 1

name_idstring
Required

The SAML NameID that identifies the user to the identity provider. Case sensitive, should not change.

Minimum length: 1

team_membershipsTeamMembership[]
Required

The teams the user is added to. At least one team membership is required so the user is visible via getUser and listUsers.

Minimum items: 1

team_idstring
Required

The team ID.

rolestring
Required

The role of the member in the team.

Available values:

  • admin
  • designer
  • member
first_namestring
Optional

The first name of the user.

last_namestring
Optional

The last name of the user.

localestring
Optional

The locale of the user, as an IETF BCP 47 language tag. Must be a supported language. Defaults to en if not provided.

Default value: en

Example request

Examples for using the /v1/users endpoint:

curl --request POST 'https://api.canva.com/admin/v1/users' \
--header 'Authorization: Bearer {token}' \
--header 'Content-Type: application/json' \
--data '{
"email": "joe@acme.com",
"display_name": "Joe Smith",
"first_name": "Joe",
"last_name": "Smith",
"locale": "en",
"saml_account": {
"idp_issuer": "https://sso.acme.com/saml/metadata",
"name_id": "joe.smith@acme.com"
},
"team_memberships": [
{
"team_id": "BAAAAAAAAA1",
"role": "member"
}
]
}'
SH
const fetch = require("node-fetch");
fetch("https://api.canva.com/admin/v1/users", {
method: "POST",
headers: {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
},
body: JSON.stringify({
"email": "joe@acme.com",
"display_name": "Joe Smith",
"first_name": "Joe",
"last_name": "Smith",
"locale": "en",
"saml_account": {
"idp_issuer": "https://sso.acme.com/saml/metadata",
"name_id": "joe.smith@acme.com"
},
"team_memberships": [
{
"team_id": "BAAAAAAAAA1",
"role": "member"
}
]
}),
})
.then(async (response) => {
const data = await response.json();
console.log(data);
})
.catch(err => console.error(err));
JS
import java.io.IOException;
import java.net.URI;
import java.net.http.*;
public class ApiExample {
public static void main(String[] args) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.canva.com/admin/v1/users"))
.header("Authorization", "Bearer {token}")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("{\"email\": \"joe@acme.com\", \"display_name\": \"Joe Smith\", \"first_name\": \"Joe\", \"last_name\": \"Smith\", \"locale\": \"en\", \"saml_account\": {\"idp_issuer\": \"https://sso.acme.com/saml/metadata\", \"name_id\": \"joe.smith@acme.com\"}, \"team_memberships\": [{\"team_id\": \"BAAAAAAAAA1\", \"role\": \"member\"}]}"))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.body());
}
}
JAVA
import requests
headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json"
}
data = {
"email": "joe@acme.com",
"display_name": "Joe Smith",
"first_name": "Joe",
"last_name": "Smith",
"locale": "en",
"saml_account": {
"idp_issuer": "https://sso.acme.com/saml/metadata",
"name_id": "joe.smith@acme.com"
},
"team_memberships": [
{
"team_id": "BAAAAAAAAA1",
"role": "member"
}
]
}
response = requests.post("https://api.canva.com/admin/v1/users",
headers=headers,
json=data
)
print(response.json())
PY
using System.Net.Http;
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://api.canva.com/admin/v1/users"),
Headers =
{
{ "Authorization", "Bearer {token}" },
},
Content = new StringContent(
"{\"email\": \"joe@acme.com\", \"display_name\": \"Joe Smith\", \"first_name\": \"Joe\", \"last_name\": \"Smith\", \"locale\": \"en\", \"saml_account\": {\"idp_issuer\": \"https://sso.acme.com/saml/metadata\", \"name_id\": \"joe.smith@acme.com\"}, \"team_memberships\": [{\"team_id\": \"BAAAAAAAAA1\", \"role\": \"member\"}]}",
Encoding.UTF8,
"application/json"
),
};
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
};
CSHARP
package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
payload := strings.NewReader(`{
"email": "joe@acme.com",
"display_name": "Joe Smith",
"first_name": "Joe",
"last_name": "Smith",
"locale": "en",
"saml_account": {
"idp_issuer": "https://sso.acme.com/saml/metadata",
"name_id": "joe.smith@acme.com"
},
"team_memberships": [
{
"team_id": "BAAAAAAAAA1",
"role": "member"
}
]
}`)
url := "https://api.canva.com/admin/v1/users"
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer {token}")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}
GO
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.canva.com/admin/v1/users",
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer {token}',
'Content-Type: application/json',
),
CURLOPT_POSTFIELDS => json_encode([
"email" => "joe@acme.com",
"display_name" => "Joe Smith",
"first_name" => "Joe",
"last_name" => "Smith",
"locale" => "en",
"saml_account" => [
"idp_issuer" => "https://sso.acme.com/saml/metadata",
"name_id" => "joe.smith@acme.com"
],
"team_memberships" => [
[
"team_id" => "BAAAAAAAAA1",
"role" => "member"
]
]
])
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if (empty($err)) {
echo $response;
} else {
echo "Error: " . $err;
}
PHP
require 'net/http'
require 'uri'
url = URI('https://api.canva.com/admin/v1/users')
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request['Authorization'] = 'Bearer {token}'
request['Content-Type'] = 'application/json'
request.body = <<REQUEST_BODY
{
"email": "joe@acme.com",
"display_name": "Joe Smith",
"first_name": "Joe",
"last_name": "Smith",
"locale": "en",
"saml_account": {
"idp_issuer": "https://sso.acme.com/saml/metadata",
"name_id": "joe.smith@acme.com"
},
"team_memberships": [
{
"team_id": "BAAAAAAAAA1",
"role": "member"
}
]
}
REQUEST_BODY
response = http.request(request)
puts response.read_body
RUBY

Success response

If successful, the endpoint returns a 200 response with a JSON body with the following parameters:

userUser

A user.

idstring

The user ID.

display_namestring
Optional

The display name of the user.

emailstring
Optional

The email address of the user.

first_namestring
Optional

The first name of the user.

last_namestring
Optional

The last name of the user.

last_active_atinteger
Optional

When the user was last active, as a Unix timestamp (in seconds since the Unix Epoch).

localestring
Optional

The locale of the user, as an IETF BCP 47 language tag.

rolestring
Optional

The role of the user in the organization.

Available values:

  • admin
  • brand_designer
  • member

Example response

{
"user": {
"id": "UAAAAAAAAA1",
"display_name": "Joe Smith",
"email": "joe@acme.com",
"first_name": "Joe",
"last_name": "Smith",
"last_active_at": 1750300000,
"locale": "en",
"role": "admin"
}
}
JSON

Error responses

400 Bad Request

codestring

A short string indicating what failed. This field can be used to handle errors programmatically. For a complete list of error codes, see Error responses.

messagestring

A human-readable description of what went wrong.

Example error response

The display name is not allowed
{
"code": "invalid_field",
"message": "The supplied display_name is not allowed."
}
JSON

403 Forbidden

codestring

A short string indicating what failed. This field can be used to handle errors programmatically. For a complete list of error codes, see Error responses.

messagestring

A human-readable description of what went wrong.

Example error responses

The SAML identity provider is not owned by the caller's organization (or doesn't exist)
{
"code": "permission_denied",
"message": "The identity provider is not owned by your organization."
}
JSON
The email domain is not a verified domain owned by the caller's organization
{
"code": "permission_denied",
"message": "The email domain is not in your organization's verified domains."
}
JSON
A target team has reached its maximum member capacity
{
"code": "max_limit_reached",
"message": "One or more provided teams have reached its maximum member capacity"
}
JSON

409 Conflict

codestring

A short string indicating what failed. This field can be used to handle errors programmatically. For a complete list of error codes, see Error responses.

messagestring

A human-readable description of what went wrong.

Example error responses

The user matching the email is not active
{
"code": "user_not_active",
"message": "The existing user matching this email is not active and can't be added."
}
JSON
{
"code": "user_pending_consent",
"message": "A previous request for this user is awaiting their consent. Wait for them to accept before retrying."
}
JSON
The SAML name ID is already in use
{
"code": "saml_name_id_not_available",
"message": "The SAML name ID {nameId} is already associated with another user."
}
JSON
An account already exists for this email
{
"code": "account_exists",
"message": "That email address is already associated with a Canva account."
}
JSON
The existing account's email is not verified
{
"code": "user_email_unverified",
"message": "The existing account for this email hasn't verified its email address, so it can't be linked to your organization."
}
JSON