> ## Documentation Index
> Fetch the complete documentation index at: https://docs.recurso.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify Magic Link (POST)

> Exchange a magic link token sent in the request body for a portal session

<Note>
  This is the preferred form of magic-link verification. The token travels in the
  JSON body rather than the query string, so it is not written to Referer headers,
  browser history, or access logs. The GET form is kept for links opened directly
  from email clients.
</Note>

## Request Body

| Field   | Type   | Description                                    |
| ------- | ------ | ---------------------------------------------- |
| `token` | string | The magic link token from the email (required) |

## Example Request

```bash theme={null}
curl -X POST "https://api.recurso.dev/portal/auth/verify" \
  -H "Content-Type: application/json" \
  -d '{ "token": "mlk_a1b2c3d4e5f6g7h8i9j0" }'
```

## Response

```json theme={null}
{
  "message": "Logged in successfully"
}
```

The 7-day session is delivered only as the httpOnly `portal_session` cookie
(plus a readable `portal_csrf` double-submit cookie) — it is never in the JSON
body. Send subsequent portal requests with `credentials: "include"`.

<Warning>
  Magic link tokens are single-use and expire after 15 minutes. An invalid or
  expired token returns `401` with the canonical error envelope.
</Warning>


## OpenAPI

````yaml POST /portal/auth/verify
openapi: 3.1.0
info:
  title: Recurso API
  version: 1.0.0
  description: |
    The Recurso billing engine REST API.

    Authenticate by passing your API key as a bearer token:

        Authorization: Bearer <api_key>

    Obtain an API key by registering a tenant via `POST /auth/register`.
    All authenticated endpoints live under the `/v1` prefix. Mutating
    endpoints support idempotency via the `Idempotency-Key` header.
  license:
    name: MIT
    identifier: MIT
servers:
  - url: https://billing.example.com
    description: >-
      Example deployment — substitute the base URL of your own Recurso
      deployment.
security:
  - bearerAuth: []
tags:
  - name: System
    description: Health, version, and API metadata
  - name: Auth
    description: Tenant registration
  - name: Plans
    description: Product catalog plans
  - name: Customers
    description: Customer management
  - name: Subscriptions
    description: Subscription lifecycle
  - name: Invoices
    description: Invoices, PDFs, and Indian GST e-invoicing
  - name: Coupons
    description: Discounts
  - name: Usage
    description: Metered usage events
  - name: Credit Notes
    description: Customer credits
  - name: Quotes
    description: Quote-to-invoice lifecycle
  - name: Webhooks
    description: Webhook endpoint management and event feed
  - name: Analytics
    description: Revenue analytics
  - name: Checkout
    description: Public hosted checkout for invoices
  - name: Payments
    description: Payment order creation
  - name: Inbound Webhooks
    description: Receivers for payment-gateway callbacks (Razorpay, Stripe)
  - name: Customer Portal
    description: Customer-facing portal — magic-link auth and session-scoped data
  - name: Developer
    description: API key management
  - name: Account
    description: Tenant account settings
  - name: Finance
    description: Ledger accounts, entries, reconciliation, and revenue recognition
  - name: Settings
    description: Tax (GST) and e-invoicing (IRP) configuration
  - name: Consents
    description: Consent records for RBI-compliant recurring billing
  - name: Referrals
    description: Customer referral program
  - name: Gifts
    description: Gift subscriptions
  - name: Mandates
    description: UPI Autopay mandates
  - name: Offline Payments
    description: Virtual accounts and manually recorded payments
  - name: Organizations
    description: Multi-entity organizations grouping several tenants
  - name: Accounting
    description: QuickBooks / Xero accounting integrations
  - name: Churn
    description: Churn risk scoring and alerts
  - name: Cancel Flows
    description: Configurable retention flows shown at cancellation time
  - name: Dunning
    description: Dunning analytics and multi-channel dunning campaigns
paths:
  /portal/auth/verify:
    post:
      tags:
        - Customer Portal
      summary: Verify a magic link (token in body) and start a session
      description: >-
        Preferred over the GET form: the token travels in the request body, not
        the URL, so it is not written to Referer headers, browser history, or
        access logs. Exchanges the token for a 7-day portal session set as the
        `portal_session` cookie.
      operationId: verifyPortalMagicLinkPost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
      responses:
        '200':
          description: Session created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
      security: []
components:
  responses:
    BadRequest:
      description: The request body or parameters are invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing or invalid credentials (API key or session cookie).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          description: Structured error detail.
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Stable machine-readable error code.
              examples:
                - validation_failed
                - unauthorized
                - forbidden
                - not_found
                - conflict
                - rate_limited
                - internal_error
                - invalid_api_key
                - key_mode_mismatch
                - over_refund
                - invoice_not_paid
                - invoice_already_paid
            message:
              type: string
              description: Human-readable explanation.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: >-
        Tenant API key obtained from `POST /auth/register` or `POST
        /v1/developer/keys`.

````