> This page is for Developers.

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

> Authenticate with username and password to access API operations that span multiple Workspaces, including refresh token management.

> **Warning**
>
> User credentials authentication is only needed temporarily for the two cross-Workspace operations listed below.
> These APIs are being updated to support [application tokens](/developers/auth/application-tokens).
> For all other API integrations, use application tokens.

User credentials authentication is currently required for:

* [Switching between Workspaces](/developers/api/workspaces/authenticate-workspace)
* [Transferring SIMs between Workspaces](/developers/api/sim/sim-migration-workspace)

> **Note**
>
> MFA is required for all accounts except trial plans and can't be disabled.
> If your account has MFA enabled, you must handle the [multi-step MFA flow](/developers/auth/multi-factor-authentication), which adds complexity to automated integrations.

## Authenticate with user credentials

To authenticate, send a POST request to `/api/v1/authenticate` with your `username` (your email address) and a SHA-1 hashed `password`.
The response includes an `auth_token` and `refresh_token`.

### Request

POST [https://cdn.emnify.net/api/v1/authenticate](https://cdn.emnify.net/api/v1/authenticate)

**`User Credentials Authentication`**

```curl User Credentials Authentication
curl -X POST https://cdn.emnify.net/api/v1/authenticate \
     -H "Content-Type: application/json" \
     -d '{
  "username": "user@service.org",
  "password": "8Y8knYSkeyYV23kd"
}'
```

**`User Credentials Authentication`**

```python User Credentials Authentication
import requests

url = "https://cdn.emnify.net/api/v1/authenticate"

payload = {
    "username": "user@service.org",
    "password": "8Y8knYSkeyYV23kd"
}
headers = {"Content-Type": "application/json"}

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

print(response.json())
```

**`User Credentials Authentication`**

```javascript User Credentials Authentication
const url = 'https://cdn.emnify.net/api/v1/authenticate';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"username":"user@service.org","password":"8Y8knYSkeyYV23kd"}'
};

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

**`User Credentials Authentication`**

```go User Credentials Authentication
package main

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

func main() {

	url := "https://cdn.emnify.net/api/v1/authenticate"

	payload := strings.NewReader("{\n  \"username\": \"user@service.org\",\n  \"password\": \"8Y8knYSkeyYV23kd\"\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))

}
```

**`User Credentials Authentication`**

```ruby User Credentials Authentication
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/authenticate")

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  \"username\": \"user@service.org\",\n  \"password\": \"8Y8knYSkeyYV23kd\"\n}"

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

**`User Credentials Authentication`**

```java User Credentials Authentication
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://cdn.emnify.net/api/v1/authenticate")
  .header("Content-Type", "application/json")
  .body("{\n  \"username\": \"user@service.org\",\n  \"password\": \"8Y8knYSkeyYV23kd\"\n}")
  .asString();
```

**`User Credentials Authentication`**

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://cdn.emnify.net/api/v1/authenticate', [
  'body' => '{
  "username": "user@service.org",
  "password": "8Y8knYSkeyYV23kd"
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

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

**`User Credentials Authentication`**

```csharp User Credentials Authentication
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/authenticate");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"username\": \"user@service.org\",\n  \"password\": \"8Y8knYSkeyYV23kd\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`User Credentials Authentication`**

```swift User Credentials Authentication
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "username": "user@service.org",
  "password": "8Y8knYSkeyYV23kd"
] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/authenticate")! 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()
```

### Response (200)

```json
{
  "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

### Hash your password with SHA-1

The API requires your password to be SHA-1 hashed before sending.
This is a legacy requirement for compatibility with existing integrations.

To generate a SHA-1 hash in your terminal:

```bash
echo -n 'your_password' | openssl sha1
```

> **Note**
>
> The `-n` flag prevents a trailing newline, which would change the hash.
> Don't include quotes around your password in the actual command if your password contains special characters.

## Refresh your auth token

After successful authentication, the server returns both an `auth_token` and a `refresh_token`.
Use the refresh token to obtain a new `auth_token` without re-entering your credentials.

### Request

POST [https://cdn.emnify.net/api/v1/authenticate](https://cdn.emnify.net/api/v1/authenticate)

**`Refresh Authentication - Renew Expired Auth Token`**

```curl Refresh Authentication - Renew Expired Auth Token
curl -X POST https://cdn.emnify.net/api/v1/authenticate \
     -H "Content-Type: application/json" \
     -d '{
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'
```

**`Refresh Authentication - Renew Expired Auth Token`**

```python Refresh Authentication - Renew Expired Auth Token
import requests

url = "https://cdn.emnify.net/api/v1/authenticate"

payload = { "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }
headers = {"Content-Type": "application/json"}

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

print(response.json())
```

**`Refresh Authentication - Renew Expired Auth Token`**

```javascript Refresh Authentication - Renew Expired Auth Token
const url = 'https://cdn.emnify.net/api/v1/authenticate';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'
};

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

**`Refresh Authentication - Renew Expired Auth Token`**

```go Refresh Authentication - Renew Expired Auth Token
package main

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

func main() {

	url := "https://cdn.emnify.net/api/v1/authenticate"

	payload := strings.NewReader("{\n  \"refresh_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"\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))

}
```

**`Refresh Authentication - Renew Expired Auth Token`**

```ruby Refresh Authentication - Renew Expired Auth Token
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/authenticate")

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  \"refresh_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"\n}"

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

**`Refresh Authentication - Renew Expired Auth Token`**

```java Refresh Authentication - Renew Expired Auth Token
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://cdn.emnify.net/api/v1/authenticate")
  .header("Content-Type", "application/json")
  .body("{\n  \"refresh_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"\n}")
  .asString();
```

**`Refresh Authentication - Renew Expired Auth Token`**

```php Refresh Authentication - Renew Expired Auth Token
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://cdn.emnify.net/api/v1/authenticate', [
  'body' => '{
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

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

**`Refresh Authentication - Renew Expired Auth Token`**

```csharp Refresh Authentication - Renew Expired Auth Token
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/authenticate");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"refresh_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Refresh Authentication - Renew Expired Auth Token`**

```swift Refresh Authentication - Renew Expired Auth Token
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = ["refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/authenticate")! 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()
```

### Response (200)

```json
{
  "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

## Token expiration

| Token           | Valid for               | Notes                |
| --------------- | ----------------------- | -------------------- |
| `auth_token`    | 240 minutes (4 hours)   | Use for API requests |
| `refresh_token` | 350 minutes (\~6 hours) | Single use only      |

> **Warning**
>
> The `refresh_token` can only be used once.
> After you use it, the server issues a new refresh token with the new auth token.
> The refresh token also becomes invalid if you log in from another client.

### Handle expired tokens

When your `auth_token` expires:

1. If your `refresh_token` is still valid, use it to get a new `auth_token`
2. If both tokens have expired, authenticate again with your username and password

To avoid interruptions, refresh your token before it expires.
Consider refreshing when the `auth_token` has less than 30 minutes remaining.