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

# Build a custom bridge

> Create a separate FiveM resource that connects your framework to HumaLike.

This example registers a custom player provider and a client interaction
provider. Add only the domains your server owns.

## Directory

```text theme={null}
resources/
├── humalike/
└── my-humalike-bridge/
    ├── fxmanifest.lua
    ├── server.lua
    └── client.lua
```

## Manifest

```lua theme={null}
fx_version 'cerulean'
game 'gta5'
lua54 'yes'

name 'my-humalike-bridge'
version '1.0.0'

dependency 'humalike'

server_script 'server.lua'
client_script 'client.lua'
```

Start it after HumaLike:

```cfg theme={null}
ensure humalike
ensure my-humalike-bridge
```

## Server provider

```lua theme={null}
local function registerProviders()
    local ok, err = exports.humalike:RegisterProvider('player', {
        name = 'my_framework',
        apiVersion = 1,
        priority = 100,

        Available = function()
            return GetResourceState('my-core') == 'started'
        end,

        GetCharacterId = function(source)
            local player = exports['my-core']:GetPlayer(source)
            return player and player.characterId or nil
        end,

        GetCharacterName = function(source)
            local player = exports['my-core']:GetPlayer(source)
            return player and player.fullName or nil
        end,

        IsCharacterLoaded = function(source)
            return exports['my-core']:GetPlayer(source) ~= nil
        end,

        HasJob = function(source, jobNames, requireDuty)
            local player = exports['my-core']:GetPlayer(source)
            if not player then return false end

            for _, name in ipairs(jobNames) do
                if player.job.name == name then
                    return not requireDuty or player.job.onDuty == true
                end
            end
            return false
        end,

        Notify = function(source, message, kind)
            TriggerClientEvent('my-ui:notify', source, message, kind)
        end,
    })

    if not ok then
        print(('[my-humalike-bridge] player provider failed: %s'):format(err))
    end
end

AddEventHandler('onResourceStart', function(resource)
    if resource == GetCurrentResourceName() then registerProviders() end
end)

AddEventHandler('humalike:integration:ready', registerProviders)
```

Do not cache a player object across callbacks. Resolve the active character from
`source` each time so reconnects and character switches are handled correctly.

## Client interaction provider

```lua theme={null}
local entries = {}

local function registerInteraction()
    exports.humalike:RegisterInteractionProvider({
        name = 'my_target',
        apiVersion = 1,
        priority = 100,

        Available = function()
            return GetResourceState('my-target') == 'started'
        end,

        watchedResources = { 'my-target' },

        Add = function(id, entity, options)
            entries[id] = { entity = entity, options = options }
            exports['my-target']:AddEntity(entity, options)
            return true
        end,

        Remove = function(id)
            local entry = entries[id]
            if not entry then return end
            exports['my-target']:RemoveEntity(entry.entity)
            entries[id] = nil
        end,

        Progress = function(durationMs, label)
            return exports['my-progress']:Run(durationMs, label) == true
        end,
    })
end

AddEventHandler('onClientResourceStart', function(resource)
    if resource == GetCurrentResourceName() then registerInteraction() end
end)

AddEventHandler('humalike:integration:ready', registerInteraction)
```

Each option contains `text`, optional `icon`, `canInteract(entity)`, and
`onSelect(entity)`. Preserve those callbacks when adapting the option to your
target system.

## Select and verify

```cfg theme={null}
set humalike_player my_framework
setr humalike_interaction my_target
```

Run `humalike_status` in both the server and client consoles. Both domains should
show `selected`, the expected provider name, and the bridge as owner.

See [Provider API](/ai-npc/integrations/provider-api) for every descriptor and
return value.

## Next

* [Add neutral player events](/ai-npc/integrations/world-events).
* [Check provider status](/ai-npc/operations#provider-states).
* [Review integration security](/ai-npc/security#trust-boundaries).
