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

# Register a new user

POST https://api.finput.com.au/api/v1/auth/register
Content-Type: application/json

Creates a new user account and sends an email verification link.

Adding a user to an existing organisation takes one of its seats, and the invited user holds it from the moment they are created, before they accept. At the organisation's user limit the invite is refused with `409`; see `seats` on `GET /api/v1/billing/entitlements`.

Reference: https://docs.finput.com.au/api/endpoints/authentication/register

## Request

### Body (application/json)

This endpoint expects a RegisterRequest.

- `email` (string, required)
- `password` (string, required)
- `first_name` (string, required)
- `last_name` (string, required)
- `user_types` (list of string, optional) — Group names to assign to the user
- `organisation_id` (integer, optional, nullable) — ID of the organisation to associate the user with

## Response

### 201

User registered successfully

- `message` (string, optional)
- `user` (User, optional)

## Errors

### 400 Bad Request Error

Validation error

- `error` (string, optional)
- `errors` (map from string to list of string, optional)

### 403 Forbidden Error

Not permitted.

- `error` (string, optional)
- `errors` (map from string to list of string, optional)

### 409 Conflict Error

The organisation is at its user limit. Wrapped in `{"errors": ...}`.

- `errors` (SeatLimitReached, optional) — The organisation is at its user limit, so the user cannot be added. `/auth/register` wraps this in `{"errors": ...}`.

## Types

### User

- `id` (integer, optional)
- `email` (string, optional)
- `first_name` (string, optional)
- `last_name` (string, optional)
- `organisation_id` (integer, optional, nullable)
- `is_admin` (boolean, optional)
- `is_active` (boolean, optional)
- `is_email_verified` (boolean, optional)
- `created` (datetime, optional)
- `groups` (list of UserGroup, optional)

### SeatLimitReached

The organisation is at its user limit, so the user cannot be added. `/auth/register` wraps this in `{"errors": ...}`.

- `error` (string, required) — A sentence a broker can read, worded for the client named in `X-Finput-Client`. A store client's sentence never mentions a plan or a way to pay.
- `reason` (enum, required)
  - Allowed values: `seat_limit_reached`
- `seats` (Seats, required) — An organisation against its user limit.

### UserGroup

- `name` (string, optional)
- `permissions` (list of UserGroupPermissionsItems, optional)

### Seats

An organisation against its user limit.

- `used` (integer, required) — Active users plus invites not yet accepted. Deactivated users are not counted.
- `limit` (integer, required, nullable) — `null` means unlimited. `used` may exceed it: the limit only refuses adding a user, so an organisation that went over it after a downgrade or trial end keeps everyone.

### UserGroupPermissionsItems

- `id` (integer, optional)
- `codename` (string, optional)

## Examples

**Request**

```json
{
  "email": "string",
  "password": "string",
  "first_name": "string",
  "last_name": "string"
}
```

**Response**

```json
{
  "message": "Registration successful. Check your email for verification.",
  "user": {
    "id": 1,
    "email": "string",
    "first_name": "string",
    "last_name": "string",
    "organisation_id": 1,
    "is_admin": true,
    "is_active": true,
    "is_email_verified": true,
    "created": "2024-01-15T09:30:00Z",
    "groups": [
      {
        "name": "string",
        "permissions": [
          {
            "id": 1,
            "codename": "string"
          }
        ]
      }
    ]
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.finput.com.au/api/v1/auth/register"

payload = {
    "email": "string",
    "password": "string",
    "first_name": "string",
    "last_name": "string"
}
headers = {"Content-Type": "application/json"}

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

print(response.json())
```

```javascript
const url = 'https://api.finput.com.au/api/v1/auth/register';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"email":"string","password":"string","first_name":"string","last_name":"string"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

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

func main() {

	url := "https://api.finput.com.au/api/v1/auth/register"

	payload := strings.NewReader("{\n  \"email\": \"string\",\n  \"password\": \"string\",\n  \"first_name\": \"string\",\n  \"last_name\": \"string\"\n}")

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

	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))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.finput.com.au/api/v1/auth/register")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"email\": \"string\",\n  \"password\": \"string\",\n  \"first_name\": \"string\",\n  \"last_name\": \"string\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.finput.com.au/api/v1/auth/register")
  .header("Content-Type", "application/json")
  .body("{\n  \"email\": \"string\",\n  \"password\": \"string\",\n  \"first_name\": \"string\",\n  \"last_name\": \"string\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.finput.com.au/api/v1/auth/register', [
  'body' => '{
  "email": "string",
  "password": "string",
  "first_name": "string",
  "last_name": "string"
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.finput.com.au/api/v1/auth/register");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"email\": \"string\",\n  \"password\": \"string\",\n  \"first_name\": \"string\",\n  \"last_name\": \"string\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "email": "string",
  "password": "string",
  "first_name": "string",
  "last_name": "string"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.finput.com.au/api/v1/auth/register")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```