> For the complete documentation index, see [llms.txt](https://docs.heyhal.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.heyhal.xyz/api-documentation/verify-signature.md).

# Verify Signature

**Endpoint:** <mark style="color:yellow;">`POST /api/v1/auth/verify-signature`</mark>

### **Description**

This endpoint verifies a wallet's signature to authenticate users. It uses Solana's signature verification system and issues a JWT token upon successful verification. The endpoint also manages user records in the database, creating new users or updating login timestamps for existing users.

**Authentication Required:** No (This is the authentication endpoint)

**Type:** None

**Request Headers**

**Content-Type:** application/json

**Body Parameters**

| Parameter     | Type   | Required | Description                                 |
| ------------- | ------ | -------- | ------------------------------------------- |
| walletAddress | string | Yes      | The Solana wallet address of the user       |
| signature     | string | Yes      | The base58-encoded signature of the message |
| message       | string | Yes      | The original message that was signed        |

**Example Request Body:**

```
{
  "walletAddress": "AaBbCc...",
  "signature": "5tBpQ...",
  "message": "Sign this message to authenticate with Pump Fun: 1234567890"
}
```

### Response

Success Response

Status Code: 200 OK

Content-Type: application/json

`{`

&#x20; `"token": "eyJhbGciOiJIUzI1NiIs..."  // JWT token valid for 24 hours`

`}`

### Error Responses

Invalid Signature

Status Code: 400 Bad Request

`{`

&#x20; `"error": "Verification failed. Invalid wallet address or signature."`

`}`

Server Error

Status Code: 500 Internal Server Error

`{`

&#x20; `"error": "An internal error occurred. Please try again later."`

`}`

**Example Usage**

Here's a complete example using JavaScript/TypeScript showing how to sign a message with a Solana wallet and verify it:

`import { Connection, PublicKey, Keypair } from "@solana/web3.js";`

`import bs58 from "bs58";`

`import nacl from "tweetnacl";`

`import { encodeUTF8, decodeUTF8 } from "tweetnacl-util";`

`async function signAndVerify(wallet: any) {  // wallet could be Phantom, Solflare, etc.`

&#x20; **`// 1. Generate a message`**

&#x20; ``const message = `Sign this message to authenticate with Pump Fun: ${Date.now()}`;``

&#x20;&#x20;

&#x20; `try {`

**`2. Request signature from wallet`**

&#x20;   `const encodedMessage = decodeUTF8(message);`

&#x20;   `const signature = await wallet.signMessage(encodedMessage, "utf8");`

&#x20;  &#x20;

&#x20; **3. Send to verification endpoint**

&#x20;`const response = await fetch('https://api.heyhal.xyz/api/v1/auth/verify-signature', {`

&#x20;     `method: 'POST',`

&#x20;     `headers: {`

&#x20;       `'Content-Type': 'application/json',`

&#x20;    `},`

&#x20;     `body: JSON.stringify({`

&#x20;       `walletAddress: wallet.publicKey.toString(),`

&#x20;       `signature: bs58.encode(signature),`

&#x20;       `message: message`

&#x20;     `})`

&#x20;   `});`

&#x20;   `if (!response.ok) {`

&#x20;     ``throw new Error(`HTTP error! status: ${response.status}`);``

&#x20;   `}`

&#x20;   `const { token } = await response.json();`

&#x20;  &#x20;

&#x20;   // Store the JWT token for future authenticated requests

&#x20;   `localStorage.setItem('auth_token', token);`

&#x20;  &#x20;

&#x20;   `return token;`

&#x20; `} catch (error) {`

&#x20;   `console.error('Authentication error:', error);`

&#x20;   `throw error;`

&#x20; `}`

`}`

**// Example with Phantom Wallet**

`async function authenticateWithPhantom() {`

&#x20; `try {`

&#x20;   `// Check if Phantom is installed`

&#x20;   `const { solana } = window as any;`

&#x20;  &#x20;

&#x20;   `if (!solana?.isPhantom) {`

&#x20;     `throw new Error('Phantom wallet is not installed!');`

&#x20;   `}`

&#x20;  &#x20;

&#x20; **Connect to wallet**

&#x20; `const response = await solana.connect();`

&#x20;   `const wallet = response.publicKey;`

&#x20;  &#x20;

&#x20; **Sign and verify**

&#x20;  `const token = await signAndVerify(solana);`

&#x20;   `console.log('Authentication successful!', token);`

&#x20;  &#x20;

&#x20;   `return token;`

&#x20; `} catch (error) {`

&#x20;   `console.error('Failed to authenticate:', error);`

&#x20;   `throw error;`

&#x20; `}`

`}`

### Implementation Notes

**Signature Verification**

The signature is verified using `tweetnacl's sign.detached.verify`

The signature must be base58 decoded before verification

The wallet address is converted to bytes for verification

<br>

**JWT Token**

The issued JWT token is valid for 24 hours (`expiresIn: "1d"`)

The token payload contains the wallet address

Use this token in the `Authorization` header for subsequent requests

<br>

**Database Operations**

New users are automatically created in the database

Existing users have their `lastLogin` timestamp updated

Initial users are created with an empty username and "user" role

<br>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.heyhal.xyz/api-documentation/verify-signature.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
