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

# Create a service

> Creates a service and returns it. `type` decides which fields apply:

- `default` creates a permanent booking page. It takes the same fields
  the API returns for one, and counts toward the Free plan's service
  limit.
- `single_use` creates a private booking link for one person. Copy one
  of your default services with `service`, or send a `title` for a
  standalone link. Send the person the returned `url`.

Booking limits, custom hours, `is_active` and the highlighted service
aren't writable through the API. Set those in Tikk.

**Scope:** `services.write`




## OpenAPI

````yaml api-reference/openapi.yaml POST /services
openapi: 3.1.0
info:
  title: Tikk API
  version: 1.0.0
  summary: Your Tikk profile, services, free time slots and bookings.
  description: |
    A REST API over one Tikk account: its public profile, its services, the
    time slots it has free and the bookings people have made with it. Reading
    is the default; writing is limited to services.

    Every response is JSON, every timestamp is ISO 8601 in UTC, and every
    request needs a bearer token: an API key (`tikk_sk_...`) or an OAuth access
    token. Each endpoint requires one scope. Rate limit: 60 requests per minute
    per credential.
  contact:
    name: Tikk support
    url: https://tikk.chat
servers:
  - url: https://app.tikk.chat/api/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Profile
    description: Your public profile and plan.
  - name: Services
    description: Your default services and single-use links.
  - name: Availability
    description: The time slots you have free.
  - name: Bookings
    description: The bookings people have made with you.
paths:
  /services:
    post:
      tags:
        - Services
      summary: Create a service
      description: |
        Creates a service and returns it. `type` decides which fields apply:

        - `default` creates a permanent booking page. It takes the same fields
          the API returns for one, and counts toward the Free plan's service
          limit.
        - `single_use` creates a private booking link for one person. Copy one
          of your default services with `service`, or send a `title` for a
          standalone link. Send the person the returned `url`.

        Booking limits, custom hours, `is_active` and the highlighted service
        aren't writable through the API. Set those in Tikk.

        **Scope:** `services.write`
      operationId: createService
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/CreateDefaultService'
                - $ref: '#/components/schemas/CreateSingleUseService'
              discriminator:
                propertyName: type
                mapping:
                  default:
                    $ref: '#/components/schemas/CreateDefaultService'
                  single_use:
                    $ref: '#/components/schemas/CreateSingleUseService'
            examples:
              default:
                summary: Default service
                value:
                  type: default
                  title: Intro call
                  durations:
                    - minutes: 30
                      price: null
                    - minutes: 60
                      price: 7500
                  location:
                    type: video
              singleUseCopy:
                summary: Single-use, copied from a default service
                value:
                  type: single_use
                  service: 48213
                  contact_name: Sam Smith
                  contact_email: sam@example.com
              singleUseStandalone:
                summary: Single-use, standalone
                value:
                  type: single_use
                  title: Follow-up with Sam
                  duration: 45
                  expires_in_days: 14
      responses:
        '201':
          description: The created service.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServiceEnvelope'
              examples:
                default:
                  $ref: '#/components/examples/DefaultService'
                singleUse:
                  $ref: '#/components/examples/SingleUseService'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: |
            The credential lacks `services.write`, or creating a default service
            would go over the Free plan's service limit (`plan_required`).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/MissingScopeError'
                  - $ref: '#/components/schemas/PlanRequiredError'
              examples:
                missingScope:
                  $ref: '#/components/examples/MissingScope'
                planRequired:
                  summary: Plan limit reached
                  value:
                    message: Upgrade to Pro to create more than 1 service.
                    error: plan_required
                    required_plan: pro
                    upgrade_url: https://app.tikk.chat/billing
        '422':
          $ref: '#/components/responses/ValidationFailed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      security:
        - bearerAuth:
            - services.write
components:
  schemas:
    CreateDefaultService:
      title: Default service
      allOf:
        - type: object
          properties:
            type:
              type: string
              const: default
          required:
            - type
        - $ref: '#/components/schemas/DefaultServiceFields'
        - required:
            - title
            - durations
    CreateSingleUseService:
      title: Single-use service
      type: object
      properties:
        type:
          type: string
          const: single_use
        service:
          type: integer
          description: |
            The `id` of one of your bookable default services to copy. The link
            offers that service's durations and location. Can't be combined
            with `duration`.
          example: 48213
        title:
          type:
            - string
            - 'null'
          maxLength: 50
          description: |
            Name of the link. Required without `service`. With `service` it
            replaces the default title, "{service} for {contact_name}".
        description:
          type:
            - string
            - 'null'
          maxLength: 500
          description: Note shown at the top of the booking page.
        contact_name:
          type:
            - string
            - 'null'
          maxLength: 100
          description: Who the link is for.
        contact_email:
          type:
            - string
            - 'null'
          format: email
          maxLength: 255
          description: When given, the contact is saved to your contacts.
        expires_in_days:
          type:
            - integer
            - 'null'
          minimum: 1
          maximum: 365
          default: 30
          description: Days until the link expires.
        duration:
          type:
            - integer
            - 'null'
          minimum: 5
          maximum: 480
          default: 30
          description: Meeting length in minutes. Standalone links only.
      required:
        - type
    ServiceEnvelope:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/Service'
      required:
        - data
    MissingScopeError:
      type: object
      properties:
        message:
          type: string
          example: Missing required scope.
        scopes:
          type: array
          description: The scope or scopes the endpoint needs.
          items:
            type: string
      required:
        - message
        - scopes
    PlanRequiredError:
      type: object
      properties:
        message:
          type: string
        error:
          type: string
          const: plan_required
        required_plan:
          type: string
          const: pro
        upgrade_url:
          type: string
          format: uri
          description: Where the account owner can upgrade.
      required:
        - message
        - error
        - required_plan
        - upgrade_url
    DefaultServiceFields:
      type: object
      description: The writable fields of a default service.
      properties:
        title:
          type: string
          maxLength: 50
          example: Intro call
        description:
          type:
            - string
            - 'null'
          maxLength: 500
          description: Shown on the booking page.
        slug:
          type: string
          maxLength: 100
          pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
          description: |
            Lowercase letters, digits and single hyphens, unique among your
            services. Custom slugs need Pro: on Free the slug is generated from
            the title and a `slug` you send is ignored.
          example: intro-call
        durations:
          type: array
          minItems: 1
          description: Replaces all durations when sent.
          items:
            type: object
            properties:
              minutes:
                type: integer
                minimum: 5
                maximum: 480
                description: Length in minutes. No repeats.
                example: 30
              price:
                type:
                  - integer
                  - 'null'
                minimum: 0
                maximum: 999999
                description: Minor currency units, so `1500` is €15.00. `null` for free.
                example: 1500
            required:
              - minutes
        is_group:
          type: boolean
          description: >-
            Several people can book the same slot. Needs Pro; `true` on Free
            returns `422`.
        max_attendees:
          type:
            - integer
            - 'null'
          minimum: 2
          maximum: 50
          description: Seat cap. Required when `is_group` is `true`.
        requires_approval:
          type: boolean
          description: >-
            Accept each booking by hand. Needs Pro; on Free, bookings are always
            accepted automatically.
        location:
          type: object
          description: Replaces the location when sent.
          properties:
            type:
              type: string
              enum:
                - video
                - in_person
                - none
            address:
              type:
                - string
                - 'null'
              maxLength: 100
          required:
            - type
    Service:
      oneOf:
        - $ref: '#/components/schemas/DefaultService'
        - $ref: '#/components/schemas/SingleUseService'
      discriminator:
        propertyName: type
        mapping:
          default:
            $ref: '#/components/schemas/DefaultService'
          single_use:
            $ref: '#/components/schemas/SingleUseService'
    Error:
      type: object
      properties:
        message:
          type: string
          description: What went wrong. Branch on the status code, not on this text.
      required:
        - message
    ValidationError:
      type: object
      properties:
        message:
          type: string
          example: The to field must be a date after or equal to from.
        errors:
          type: object
          description: Failed rules, keyed by parameter name.
          additionalProperties:
            type: array
            items:
              type: string
          example:
            to:
              - The to field must be a date after or equal to from.
      required:
        - message
        - errors
    DefaultService:
      title: Default service
      type: object
      description: A permanent booking page, such as "Intro call".
      properties:
        type:
          type: string
          const: default
        id:
          type: integer
          description: >-
            Pass it to `/services/{id}`, as `service` on `/availability`, and as
            `service` on `POST /services` to copy it into a single-use service.
          example: 48213
        slug:
          type: string
          description: The last segment of the service's booking URL.
          example: intro-call
        title:
          type: string
          example: Intro call
        description:
          type:
            - string
            - 'null'
          description: Shown on the booking page.
        is_default:
          type: boolean
          description: >-
            `true` for the one service your booking page highlights. Not the
            same as `type: default`.
        is_group:
          type: boolean
          description: '`true` if several people can book the same slot.'
        max_attendees:
          type:
            - integer
            - 'null'
          description: Seat cap for a group service. `null` otherwise.
        requires_approval:
          type: boolean
          description: '`true` if you accept each booking by hand.'
        durations:
          type: array
          description: The durations a booker can pick.
          items:
            $ref: '#/components/schemas/Duration'
        location:
          oneOf:
            - $ref: '#/components/schemas/ServiceLocation'
            - type: 'null'
          description: Where the meeting takes place.
        embed_url:
          type:
            - string
            - 'null'
          format: uri
          description: The service's booking URL. `null` until you pick a username.
          example: https://tikk.chat/jane/intro-call
      required:
        - type
        - id
        - slug
        - title
        - description
        - is_default
        - is_group
        - max_attendees
        - requires_approval
        - durations
        - location
        - embed_url
    SingleUseService:
      title: Single-use service
      type: object
      description: >-
        A private booking link for one person, spent once it's booked. Bookings
        made through one are accepted straight away and never paid.
      properties:
        type:
          type: string
          const: single_use
        id:
          type: integer
          description: >-
            Pass it to `GET` and `DELETE /services/{id}`, and as `service` on
            `/availability`.
          example: 73920
        title:
          type: string
          description: Name of the link.
          example: Intro call for Sam Smith
        description:
          type:
            - string
            - 'null'
          description: Note shown at the top of the booking page.
        status:
          type: string
          enum:
            - open
            - used
            - expired
            - disabled
          description: |
            `open` can be booked. `used` has been booked and no longer works.
            `expired` passed `expires_at` without a booking. `disabled` was
            switched off in Tikk. A used link stays `used`, even after it
            expires or is switched off.
          example: open
        url:
          type:
            - string
            - 'null'
          format: uri
          description: The private link to send. `null` until you pick a username.
          example: https://tikk.chat/jane/Xk3pQ9aB2c/intro-call-for-sam-smith
        contact:
          oneOf:
            - $ref: '#/components/schemas/Person'
            - type: 'null'
          description: Who the link is for. `null` when no contact was given.
        durations:
          type: array
          description: The offered durations.
          items:
            $ref: '#/components/schemas/Duration'
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When the link stops working.
        created_at:
          type:
            - string
            - 'null'
          format: date-time
      required:
        - type
        - id
        - title
        - description
        - status
        - url
        - contact
        - durations
        - expires_at
        - created_at
    Duration:
      type: object
      properties:
        minutes:
          type: integer
          description: Length in minutes.
          example: 30
        price:
          type:
            - integer
            - 'null'
          description: >-
            Price in minor currency units, so `7500` is €75.00. `null` when
            free. Always `null` on single-use services, which are never paid.
          example: 7500
        currency:
          type: string
          description: ISO 4217 code. Always `EUR`.
          example: EUR
      required:
        - minutes
        - price
        - currency
    ServiceLocation:
      type: object
      properties:
        type:
          type: string
          enum:
            - video
            - in_person
            - none
          example: video
        label:
          type: string
          description: Human-readable name of the location type.
          example: Video Meeting
        address:
          type:
            - string
            - 'null'
          description: Only when you show the address publicly. `null` otherwise.
      required:
        - type
        - label
        - address
    Person:
      type: object
      properties:
        name:
          type:
            - string
            - 'null'
          example: Sam Smith
        email:
          type:
            - string
            - 'null'
          format: email
          example: sam@example.com
      required:
        - name
        - email
  headers:
    X-RateLimit-Limit:
      description: Requests allowed per minute for this credential.
      schema:
        type: integer
        example: 60
    X-RateLimit-Remaining:
      description: Requests left in the current minute.
      schema:
        type: integer
        example: 59
  examples:
    DefaultService:
      summary: Default service
      value:
        data:
          type: default
          id: 48213
          slug: intro-call
          title: Intro call
          description: A first chat to see if we're a fit.
          is_default: true
          is_group: false
          max_attendees: null
          requires_approval: true
          durations:
            - minutes: 30
              price: null
              currency: EUR
            - minutes: 60
              price: 7500
              currency: EUR
          location:
            type: video
            label: Video Meeting
            address: null
          embed_url: https://tikk.chat/jane/intro-call
    SingleUseService:
      summary: Single-use service
      value:
        data:
          type: single_use
          id: 73920
          title: Intro call for Sam Smith
          description: null
          status: open
          url: https://tikk.chat/jane/Xk3pQ9aB2c/intro-call-for-sam-smith
          contact:
            name: Sam Smith
            email: sam@example.com
          durations:
            - minutes: 30
              price: null
              currency: EUR
          expires_at: '2026-10-31T09:00:00+00:00'
          created_at: '2026-10-01T09:00:00+00:00'
    MissingScope:
      summary: Missing scope
      value:
        message: Missing required scope.
        scopes:
          - services.write
  responses:
    Unauthorized:
      description: No credential was sent, or it's revoked, expired or unknown.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Unauthenticated.
    ValidationFailed:
      description: One or more parameters failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationError'
    TooManyRequests:
      description: More than 60 requests in a minute for this credential.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Too Many Attempts.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        An API key from **Settings → API Keys** (starts with `tikk_sk_`), or an
        OAuth access token for a partner app. Both carry scopes; each endpoint
        lists the one it needs.

````