# Trigger CC via HTTP

{% hint style="info" %}
**Version Compatibility**

This feature is compatible with **v1.1.0** and later of CloudCode.
{% endhint %}

You can trigger a CloudCode Task via HTTP. This is referred to as a **Web Task**.

In this way, CloudCode can be used to create a JSON-based HTTP API.

The task will need to have the "Web Request" trigger enabled in the task's configuration.

The hostnames (with `.poweredbyjourney.com` suffix) for an app's web-triggered CloudCode tasks are configured by editing an app deployment in the Deployments workspace in OXIDE.

After deploying, enabled tasks are accessible on `https://<your_app_deployment_hostname>.poweredbyjourney.com/<task>`.

Note that domain names are globally unique, so care is required when choosing hostnames for each of your apps and app deployments. Consider using the organization name, app name, and a `-testing` or `-staging` suffix for the corresponding deployments. e.g.

* `https://acme-my-app-testing.poweredbyjourney.com/my_web_task`,
* `https://acme-my-app-staging.poweredbyjourney.com/my_web_task`,
* `https://acme-my-app.poweredbyjourney.com/my_web_task`.

### A Minimal Example

Here's a minimal example of a task that can handle GET requests:

```javascript
// This MUST be defined, and either return access.unauthorized() or access.authorized().
export async function authenticate({request, access}) {
    return access.authorized();
}

export async function get({params, request, response}) {
    return {hello: 'world'};
}
```

### Authentication Example

This is an example implementation of authentication for a POST request.

```javascript
// This MUST either return access.unauthorized() or access.authorized().
// Any object passed into access.authorized() will be available as authContext in the task.
// The access.unauthorized({status: 403, body: "Message"}) call allows for custom error responses.
// If this function is not defined, an error is thrown, or
// a different value is returned, the request fails with 500.
export async function authenticate({request, access, params}) {
    // Authentication is up to the developer. In this example,
    // We check for a token in the Authorization header, and
    // do a database lookup based on this.
    const auth = request.header('Authorization');
    const match = /^Bearer (\w+)$/.exec(auth);
    if(match == null) {
       return access.unauthorized();
    }
    const token = match[1];
    const client = await DB.api_client.first('token = ?', token);
    if(client == null) {
        return access.unauthorized();
        // The response can also be customized:
        //   return access.unauthorized({body: 'Forbidden', status: 403});
        // or JSON:
        //   return access.unauthorized({body: {message: 'Forbidden'}, status: 403});
    } else {
        // The parameter here (client) will be available as `authContext` in
        // in handler.
        return access.authorized(client);
    }
}

export async function post({params, request, response, authContext}) {
    const client = authContext;
    return {user: client.name};
}
```

### Request and Response Details

```javascript
export async function post({params, request, response, authContext}) {
    // params is a convenience object containing all the parameters for the request. It is a combination of:
    // 1. Query string parameters
    // 2. Request body, in the case of a JSON POST request.
    
    // request details:
    request.method; // GET, POST, etc
    request.url.href; // The full URL for the request: https://developer.mozilla.org/en-US/docs/Web/API/URL
    request.headers; // Headers object: https://developer.mozilla.org/en-US/docs/Web/API/Headers
  
    request.hostname; // full hostname of the request
    request.subdomain; // just the subdomain
    request.searchParams; // Query parameters, as URLSearchParams: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
    request.path; // path of the request, excluding hostname and query parameters
    
    request.json(); // Body of the request as JSON, null for GET, or throw an Error if invalid JSON
    request.text(); // Body of the request as a string, or null for GET
    request.header(key); // Value of a request header
    
    
    // response details (all optional, only if more control is desired):
    response.contentType('application/json'); // Set the Content-Type header
    response.status(code); // Set the response status code
    response.body(string | json); // Set the response body
    
    // If a value is returned, it is automatically used as the response body.
    // Content-Type is automatically set in this case.
    const body = {hello: 'world'};
    return body;
  
    // If no value is returned, the value passed into `response.body` will be used as the body.
}
```

### Limitations

* Custom domains are not supported - only `*.poweredbyjourney.com`.
* Custom paths are not supported.
* Only `application/json` is supported for request format, and `application/json`, `text/html` or `text/plain` for response format. Binary data specifically is not supported.

{% hint style="warning" %}
**Web tasks running longer than 60 seconds**

It is recommended that developers keep web tasks running for *fewer than 60 seconds* in total.&#x20;

Once a task reaches more than 60 seconds the behavior is as follows:

* After 60 seconds, another invocation is scheduled in a new process, with a new trace ID, while the previous one is still running. This repeats for 3x invocations total.&#x20;
* After 3 minutes, the client gets a 504 Gateway Timeout response.&#x20;
* If one of the retries happens to complete in less than 60 seconds, and before a total of 3 minutes, the client gets that response.&#x20;
* Each invocation shows "COMPLETED", even though the client never saw the response.
  {% endhint %}
