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

# Enrich a contact's email

> Attempts to find a verified email for the given contact. This call is synchronous: it queries several enrichment providers in sequence and only responds once the search completes, which can take from a few seconds up to several minutes. Configure your HTTP client timeout accordingly. Each email enrichment that returns an email consumes 1 credit; failed enrichments do not consume any credit.



## OpenAPI

````yaml POST /v1/contact/{id}/enrich/email
openapi: 3.0.0
info:
  title: Gojiberry AI - External API
  description: >-

    # Gojiberry AI External API


    This API allows you to programmatically access your Gojiberry AI data and
    integrate it with your applications.


    ## How it works


    Gojiberry runs your outreach as a pipeline of three building blocks:


    1. **Source agent — the lead finder.** A source agent continuously sources
    leads matching your targeting criteria (job titles, industries, locations,
    signals, ...) and scores them with AI.

    2. **List — where the leads land.** Every source agent is linked to a list
    (`listId` is required when creating an agent): the leads it finds are
    imported as contacts into that list. Lists can also be filled by importing
    contacts yourself.

    3. **Campaign — the outreach.** A campaign uses one or more lists as its
    lead source (`listIds` is required when creating a campaign) and runs its
    steps (LinkedIn invitations, messages, emails, ...) on the contacts of those
    lists, sending through your LinkedIn and/or email seats. Once activated, the
    campaign runs the outreach automatically on a daily basis.


    **Terminology.** In the Gojiberry platform, an **Agent** is a *campaign
    agent* — the campaign that performs the outreach. A **source agent** is the
    lead finder that sources the leads. The two are linked through a **list**:
    the source agent imports the leads it finds into the list, and the campaign
    agent reaches out to the contacts of that list.


    **Best practice: create a Full Cycle agent.** Always create the three
    resources in this order — first the list, then the source agent that fills
    it, then the campaign that reaches out to it. Together, the list + source
    agent + campaign agent form a **Full Cycle agent**: leads are sourced and
    outreached automatically, end to end. If you create a source agent without a
    campaign agent, it shows up as a **legacy agent** in the platform: it only
    sources leads and does **no outreach**. So it's best practice to always
    create all three.


    A typical setup through the API:


    ```text

    POST /v1/list                  # 1. create the list

    POST /v1/agent                 # 2. create the source agent (lead finder)
    linked to that list (listId)

    POST /v1/campaign              # 3. create the campaign agent using that
    list as lead source (listIds)

    GET  /v1/campaign/{id}/logs    # 4. follow the outreach activity

    ```


    ## Authentication


    All API requests require authentication using a Bearer token. To get your
    API key:


    1. **Log in to your Gojiberry AI account** at
    [https://app.gojiberry.ai](https://app.gojiberry.ai)

    2. **Navigate to Settings** → **API** section

    3. **Click "Create API Key"** to generate a new token

    4. **Copy the API key** when it's displayed (you won't be able to see it
    again)

    5. **Use the token** in your requests by adding the header: `Authorization:
    Bearer YOUR_API_KEY`


    ## Impersonation (organization owners)


    If your API key belongs to the **owner of an organization**, you can perform
    any API call on behalf of another member of your organization by adding the
    `x-impersonate-user-id` header with the member's user ID:


    ```bash

    # Get the contacts of the organization member with user ID 123

    curl -H "Authorization: Bearer YOUR_API_KEY" \
         -H "x-impersonate-user-id: 123" \
         https://ext.gojiberry.ai/v1/contact
    ```


    When impersonating, the request behaves exactly as if it was made by that
    member: you read and write their data (contacts, campaigns, lists, unibox,
    etc.).


    Rules:

    - Your API key must belong to the **organization owner**; other members
    cannot impersonate.

    - The impersonated user must belong to the **same organization** as the API
    key user.

    - Requests that don't meet these rules are rejected with a `401
    Unauthorized`.


    Tip: use `GET /v1/organization/members` to list the members of your
    organization and their user IDs, and `GET /v1/user/me` to verify which user
    the current request acts as.


    ## Rate Limits


    - **100 requests per minute** per account. Short bursts above this are
    tolerated.

    - **100,000 requests per day** per account.

    - Limits are shared by all API keys and AI app (MCP) connections of the same
    user, including calls made on behalf of organization members with
    `x-impersonate-user-id`.

    - Requests over a limit return `429 Too Many Requests` with a `Retry-After`
    header (in seconds). Wait that long before retrying.

    - Some endpoints have their own lower limit, stated in their description.


    ## Base URL

    - **Production**: `https://ext.gojiberry.ai`


    ## Endpoints


    ### Contacts

    - `GET /v1/contact` - Get all contacts with filtering and pagination

    - `GET /v1/contact/{id}` - Get a specific contact

    - `GET /v1/contact/health` - Health check endpoint


    ## Example Usage


    ```bash

    # Get all contacts

    curl -H "Authorization: Bearer YOUR_API_KEY" \
         https://ext.gojiberry.ai/v1/contact

    # Get contacts with filtering

    curl -H "Authorization: Bearer YOUR_API_KEY" \
         "https://ext.gojiberry.ai/v1/contact?search=john&agent=1&dateFrom=2024-01-01"

    # Get a specific contact

    curl -H "Authorization: Bearer YOUR_API_KEY" \
         https://ext.gojiberry.ai/v1/contact/123
    ```


    ## Webhooks


    Gojiberry allows you to receive real-time notifications when new contacts
    are created or updated by configuring a webhook URL.


    ### How to Configure Webhooks


    1. **Log in to your Gojiberry AI account** at
    [https://app.gojiberry.ai](https://app.gojiberry.ai)

    2. **Navigate to the Integrations page** at
    [https://app.gojiberry.ai/integrations](https://app.gojiberry.ai/integrations)

    3. **Add a Webhook integration** by clicking the Webhook option

    4. **Enter your webhook URL** where you want to receive notifications

    5. **Save the configuration**


    Once configured, Gojiberry will send POST requests to your webhook URL
    whenever:

    - A new contact is created

    - A contact is updated

    - Other relevant events occur in your account


    The webhook payload will include the full contact data in JSON format. Each
    webhook request includes a custom header `x-gojiberry-user-id` containing
    your user ID for verification purposes.


    ### Verifying Webhook Signatures (optional)


    You can optionally configure a signing secret on your Webhook integration.
    When a secret is set, every webhook request also includes:


    - `x-gojiberry-timestamp`: the Unix timestamp (in seconds) at which the
    request was sent

    - `x-gojiberry-signature`: `sha256=` followed by the hex-encoded HMAC-SHA256
    of `{timestamp}.{rawBody}` computed with your signing secret


    To verify a request:


    1. Read the raw request body as a string (do not re-serialize parsed JSON).

    2. Concatenate the value of `x-gojiberry-timestamp`, a `.` character, and
    the raw body.

    3. Compute the HMAC-SHA256 of that string using your signing secret,
    hex-encoded, and prefix it with `sha256=`.

    4. Compare it to `x-gojiberry-signature` using a constant-time comparison.

    5. Reject requests whose timestamp is too old (we recommend a 5 minute
    tolerance) to prevent replay attacks.


    Example in Node.js:


    ```js

    const crypto = require('crypto');


    function verifyGojiberrySignature(rawBody, headers, secret) {
      const timestamp = headers['x-gojiberry-timestamp'];
      const signature = headers['x-gojiberry-signature'];
      if (!timestamp || !signature) return false;
      if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
      const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(timestamp + '.' + rawBody).digest('hex');
      return expected.length === signature.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
    }

    ```


    If no signing secret is configured, these headers are not sent.


    ## Support


    For API support, please contact us at
    [hello@gojiberry.ai](mailto:hello@gojiberry.ai)
          
  version: '1.0'
  contact: {}
servers:
  - url: https://ext.gojiberry.ai
    description: Production
security: []
tags: []
paths:
  /v1/contact/{id}/enrich/email:
    post:
      tags:
        - Contacts
      summary: Enrich a contact's email
      description: >-
        Attempts to find a verified email for the given contact. This call is
        synchronous: it queries several enrichment providers in sequence and
        only responds once the search completes, which can take from a few
        seconds up to several minutes. Configure your HTTP client timeout
        accordingly. Each email enrichment that returns an email consumes 1
        credit; failed enrichments do not consume any credit.
      operationId: ContactExternalController_enrichEmail
      parameters:
        - name: x-impersonate-user-id
          in: header
          description: >-
            ID of an organization member to act on behalf of. The API key user
            must be the owner of the organization and the target user must
            belong to the same organization.
          required: false
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: number
      responses:
        '200':
          description: Contact updated with the enriched email
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '404':
          description: Contact not found
        '422':
          description: No email could be found for this contact (no credit consumed)
      security:
        - bearer: []
components:
  schemas:
    Contact:
      type: object
      properties:
        id:
          type: number
          description: Unique identifier for the contact
        firstName:
          type: string
          description: First name of the contact
        lastName:
          type: string
          description: Last name of the contact
        fullName:
          type: string
          description: Full name (auto-generated from firstName and lastName)
          readOnly: true
        profileBaseline:
          type: string
          description: Profile baseline information
        location:
          type: string
          description: Location of the contact
        companyLocation:
          type: string
          description: Location of the contact company
        jobTitle:
          type: string
          description: Job title of the contact
        positionDates:
          type: string
          description: Position dates information
        company:
          type: string
          description: Company name
        companySize:
          type: string
          description: Company size
        companyUrl:
          type: string
          description: Company URL
        website:
          type: string
          description: Website URL
        industry:
          type: string
          description: Industry
        email:
          type: string
          description: Primary email address
        email_2:
          type: string
          description: Secondary email address
        email_3:
          type: string
          description: Tertiary email address
        secondEmail:
          type: string
          description: Second email alias
        thirdEmail:
          type: string
          description: Third email alias
        phone:
          type: string
          description: Primary phone number
        phone_2:
          type: string
          description: Secondary phone number
        phone_3:
          type: string
          description: Tertiary phone number
        secondPhone:
          type: string
          description: Second phone alias
        thirdPhone:
          type: string
          description: Third phone alias
        profileId:
          type: string
          description: LinkedIn profile ID
        linkedinMemberId:
          type: number
          description: Numeric LinkedIn member ID decoded from the profile URN
        profileUrl:
          type: string
          description: LinkedIn profile URL
        picture:
          type: string
          description: Profile picture URL
        openToWork:
          type: boolean
          description: Open to work status
        skills:
          description: Array of skills
          type: array
          items:
            type: string
        linkedinIdentifier:
          type: string
          description: LinkedIn identifier for the profile
        note:
          type: string
          description: Additional notes about the contact
        user_note:
          type: string
          description: User personal notes about the contact
        intent:
          type: string
          description: Intent or activity description
        intent_scoring:
          type: number
          description: AI scoring for intent relevance between 0 and 3
        total_scoring:
          type: number
          description: Total scoring (intent_scoring + scoring)
        intent_type:
          type: object
          description: Type or category of the intent
        intent_keyword:
          type: string
          description: Keyword extracted from the intent
        scoring:
          type: number
          description: AI scoring result between 0 and 1 (0 = no match, 1 = perfect match)
        score_reasoning:
          type: string
          description: Reasoning for the AI scoring result
        bounced:
          type: boolean
          description: Whether the contact email has bounced
          default: false
        unsubscribed:
          type: boolean
          description: Whether the contact has unsubscribed from emails
          default: false
        redListed:
          type: boolean
          description: Whether the contact is on the global GDPR red list (do-not-contact)
          default: false
        blocked:
          type: boolean
          description: Whether the contact matches the organization people blocklist
          default: false
        companyBlocked:
          type: boolean
          description: >-
            Set once a campaign pass skipped the contact because its company is
            on the organization blocklist
          default: false
        emailEnriched:
          type: boolean
          description: Whether email has been enriched
          default: false
        phoneEnriched:
          type: boolean
          description: Whether phone has been enriched
          default: false
        enrichingEmail:
          type: boolean
          description: Whether an email enrichment job currently holds the billing lease
          default: false
        enrichingPhone:
          type: boolean
          description: Whether a phone enrichment job currently holds the billing lease
          default: false
        fit:
          type: string
          description: Contact fit classification
          enum:
            - qualified
            - unknown
            - out-of-scope
        rejectedAt:
          format: date-time
          type: string
          description: When this contact was rejected
          nullable: true
        rejectedByUserId:
          type: number
          description: User who rejected this contact
          nullable: true
        rejectionReason:
          type: string
          description: Stable rejection reason
          nullable: true
        rejectionReasonText:
          type: string
          description: Free-text reason when Other was submitted with text
          nullable: true
        deepResearchStartedAt:
          format: date-time
          type: string
          description: When the latest deep research started for this contact
        deepResearchEndedAt:
          format: date-time
          type: string
          description: When the latest deep research ended for this contact
        email_template:
          type: object
          description: Email template in JSON format with subject and body
        linkedin_template:
          type: string
          description: LinkedIn template text
        personalizedMessages:
          description: Personalized messages for the contact
          type: array
          items:
            type: string
        destinationStatus:
          description: Destination sync status information
          type: array
          items:
            type: string
        customFields:
          type: object
          description: >-
            Custom fields imported from CSV, keyed by normalized snake_case name
            (rendered with the custom_ prefix)
        campaignStatus:
          description: >-
            Campaign status information. Useful to know what is the achieved
            step of the contact in the campaign — i.e. how far the contact has
            progressed through the campaign's sequence. The stepNumber matches
            the campaign's step numbers (the steps defined on the campaign), so
            a stepNumber of 2 means the contact has reached step 2 of that
            campaign.
          type: array
          items:
            type: string
        signals:
          description: >-
            Every signal recorded for the contact, most recent first (agents and
            LinkedIn pools)
          type: array
          items:
            type: string
        userId:
          type: number
          description: User ID who owns this contact
        user:
          description: Associated user account
          allOf:
            - $ref: '#/components/schemas/Account'
        agentId:
          type: number
          description: Associated agent ID
        agent:
          description: Associated agent
          allOf:
            - $ref: '#/components/schemas/Agent'
        listId:
          type: number
          description: Associated list ID
        list:
          description: Associated list
          allOf:
            - $ref: '#/components/schemas/List'
        readyForCampaign:
          type: boolean
          description: Ready for campaign flag
          default: false
        hasStartedCampaign:
          type: boolean
          description: Has started campaign flag
          default: false
        state:
          type: string
          description: Contact state
          enum:
            - finished
            - paused
            - 1stnetwork
            - excluded
            - onhold
        createdAt:
          format: date-time
          type: string
          description: Creation timestamp
        updatedAt:
          format: date-time
          type: string
          description: Last update timestamp
      required:
        - id
        - firstName
        - lastName
        - fullName
        - bounced
        - unsubscribed
        - redListed
        - blocked
        - companyBlocked
        - emailEnriched
        - phoneEnriched
        - enrichingEmail
        - enrichingPhone
        - userId
        - user
        - readyForCampaign
        - hasStartedCampaign
        - createdAt
        - updatedAt
    Account:
      type: object
      properties: {}
    Agent:
      type: object
      properties:
        id:
          type: number
          description: Unique identifier
        name:
          type: string
          description: Display name of the agent
        targetJobTitles:
          description: List of target job titles to prospect. Part of the ICP.
          type: array
          items:
            type: string
        targetIndustries:
          description: List of target industries to prospect. Part of the ICP.
          type: array
          items:
            type: string
        targetCompanySizes:
          description: >-
            List of target company sizes (e.g. "1-10", "11-50"). Part of the
            ICP.
          type: array
          items:
            type: string
        targetLocations:
          description: List of target geographic locations. Part of the ICP.
          type: array
          items:
            type: string
        targetCompanyTypes:
          description: List of target company types (e.g. "Public", "Private")
          type: array
          items:
            type: string
        additionalCriteria:
          type: string
          description: Additional free-text targeting criteria
          nullable: true
        variables:
          description: >-
            Variables used by the agent to find high intent leads. (Keywords,
            Competitor Page URL, etc.)
          nullable: true
          type: array
          items:
            type: string
        ignoredCompanies:
          description: Company names to exclude from prospecting. Part of the ICP.
          nullable: true
          type: array
          items:
            type: string
        mandatoryKeywords:
          description: Keywords that must be present in a lead profile to be included
          nullable: true
          type: array
          items:
            type: string
        agentType:
          type: string
          description: 'Type of agent: "autopilot", "lookalike" or "website-visitor"'
          example: autopilot
        scriptInstalled:
          type: boolean
          description: >-
            Whether the visitor tracking script is installed on the website
            (website-visitor agents)
          default: false
        trackingScriptId:
          type: string
          description: >-
            Snitcher tracking script ID, used as the profileId in the tracking
            snippet (website-visitor agents)
          nullable: true
        maxCreditUsage:
          type: number
          description: Max number of credits per month
          nullable: true
          example: 200
        currentCreditUsage:
          type: number
          description: >-
            Credits consumed so far in the current period (e.g. website-visitor
            company identifications)
          default: 0
        lastRun:
          format: date-time
          type: string
          description: Timestamp of the last agent execution
          nullable: true
        paused:
          type: boolean
          description: >-
            Whether the agent is paused and will not run on its next scheduled
            time
          default: false
        excludeServiceProviders:
          type: boolean
          description: Whether to exclude service provider companies from prospecting
          default: false
        skipIcpFilter:
          type: boolean
          description: >-
            Whether to skip the ICP (Ideal Customer Profile) filter when
            sourcing leads
          default: false
        includeOpenToWorkProfiles:
          type: boolean
          description: Whether to include LinkedIn profiles marked as open to work
          default: false
        leadWaterfall:
          type: boolean
          description: >-
            Whether to use the lead waterfall strategy (cascade through multiple
            sources)
          default: true
        minLeadScore:
          type: number
          description: Minimum lead score threshold (0.00–1.00) required to include a lead
          nullable: true
          example: 0.75
        userId:
          type: number
          description: ID of the account (user) who owns this agent
        listId:
          type: number
          description: ID of the list this agent sources contacts into
          nullable: true
        createdAt:
          format: date-time
          type: string
          description: Creation timestamp
        updatedAt:
          format: date-time
          type: string
          description: Last update timestamp
      required:
        - id
        - name
        - targetJobTitles
        - targetIndustries
        - targetCompanySizes
        - targetLocations
        - targetCompanyTypes
        - agentType
        - scriptInstalled
        - currentCreditUsage
        - paused
        - excludeServiceProviders
        - skipIcpFilter
        - includeOpenToWorkProfiles
        - leadWaterfall
        - userId
        - createdAt
        - updatedAt
    List:
      type: object
      properties:
        id:
          type: number
          description: Unique identifier for the list
        name:
          type: string
          description: Name of the list
        userId:
          type: number
          description: User ID who owns this list
        user:
          description: Associated user account
          allOf:
            - $ref: '#/components/schemas/Account'
        campaignId:
          type: number
          description: Campaign ID associated with this list
        campaign:
          description: Associated campaign
          allOf:
            - $ref: '#/components/schemas/Campaign'
        customFields:
          description: >-
            Custom field definitions imported into this list (append-only union
            across imports)
          type: array
          items:
            type: string
        createdAt:
          format: date-time
          type: string
          description: Creation timestamp
        updatedAt:
          format: date-time
          type: string
          description: Last update timestamp
      required:
        - id
        - name
        - userId
        - user
        - createdAt
        - updatedAt
    Campaign:
      type: object
      properties:
        id:
          type: number
          description: Unique identifier for the campaign
        name:
          type: string
          description: Name of the campaign
        steps:
          type: array
          description: Campaign steps
          items:
            type: object
        userId:
          type: number
          description: User ID who owns this campaign
        active:
          type: boolean
          description: Whether the campaign is active
          default: true
        excludeFirstDegreeFromCampaigns:
          type: boolean
          description: Exclude first degree connections from campaigns
          default: true
        splitLinkedinMessagesIntoConversation:
          type: boolean
          description: >-
            When enabled, LinkedIn message steps may be sent as multiple shorter
            messages when it feels natural
          default: false
        isManual:
          type: boolean
          description: Whether the campaign is manual
          default: false
        skipInvitationAfterDays:
          type: number
          description: >-
            Number of days after which a pending invitation is skipped and only
            email steps are sent
        language:
          type: string
          description: Campaign language
          nullable: true
        tone:
          type: string
          description: Campaign tone
          enum:
            - professional
            - direct
            - conversational
        goal:
          type: string
          description: Campaign goal
          enum:
            - conversations
            - demos
        linkedinSeatId:
          type: number
          description: LinkedIn seat ID for this campaign
        createdAt:
          format: date-time
          type: string
          description: Creation timestamp
        updatedAt:
          format: date-time
          type: string
          description: Last update timestamp
      required:
        - id
        - name
        - userId
        - active
        - excludeFirstDegreeFromCampaigns
        - splitLinkedinMessagesIntoConversation
        - isManual
        - createdAt
        - updatedAt
  securitySchemes:
    bearer:
      scheme: bearer
      bearerFormat: JWT
      type: http
      description: 'API key from Settings → API. Send it as Authorization: Bearer <key>.'

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.