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

# Bitbucket

export const IntegrationSuccess = ({integrationName}) => {
  return <p>
      <b>{integrationName} connected!</b><br />You can now use{" "}
      <a href="/building-with-clark">Clark to build</a> with {integrationName}.
    </p>;
};

export const DynamicIntegrationActions = ({integrationId, maxActions = 20}) => {
  const [actions, setActions] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [showAll, setShowAll] = useState(false);
  useEffect(() => {
    const fetchIntegrationSpec = async () => {
      try {
        setLoading(true);
        const response = await fetch(`https://integrations.superblocks.com/openapi/${integrationId}.yaml`);
        if (!response.ok) {
          throw new Error(`Failed to fetch integration spec: ${response.status}`);
        }
        const yamlText = await response.text();
        const actions = parseActionsFromYaml(yamlText);
        setActions(actions);
        setError(null);
      } catch (err) {
        setError(err.message);
        setActions(getFallbackActions(integrationId));
      } finally {
        setLoading(false);
      }
    };
    fetchIntegrationSpec();
  }, [integrationId]);
  const parseActionsFromYaml = yamlText => {
    const actions = [];
    const summaryRegex = /summary:[ \t]+(.+)/g;
    let match;
    while ((match = summaryRegex.exec(yamlText)) !== null) {
      const summary = match[1].trim();
      if (summary && summary.length > 0) {
        actions.push({
          summary: summary,
          description: `API action: ${summary.toLowerCase()}`
        });
      }
    }
    return actions.slice(0, 50);
  };
  const getFallbackActions = integrationId => {
    const fallbackActions = {
      slack: [{
        summary: 'Send Message',
        description: 'Send a message to a Slack channel or user'
      }, {
        summary: 'Get Channel Info',
        description: 'Retrieve information about a Slack channel'
      }, {
        summary: 'List Channels',
        description: 'Get a list of channels in the workspace'
      }, {
        summary: 'Get User Info',
        description: 'Retrieve information about a Slack user'
      }, {
        summary: 'List Users',
        description: 'Get a list of users in the workspace'
      }],
      github: [{
        summary: 'Get Repository',
        description: 'Retrieve information about a repository'
      }, {
        summary: 'List Issues',
        description: 'Get a list of issues in a repository'
      }, {
        summary: 'Create Issue',
        description: 'Create a new issue in a repository'
      }, {
        summary: 'List Pull Requests',
        description: 'Get a list of pull requests'
      }, {
        summary: 'Create Pull Request',
        description: 'Create a new pull request'
      }]
    };
    return fallbackActions[integrationId] || [{
      summary: 'Generic Action',
      description: 'Perform actions with this integration'
    }];
  };
  if (loading) {
    return <div>
        <p>Loading actions for {integrationId}...</p>
        <div style={{
      display: 'inline-flex',
      flexWrap: 'wrap',
      gap: '0.5em',
      marginTop: '8px'
    }}>
          {[...Array(8)].map((_, i) => <div key={i} style={{
      alignItems: 'center',
      padding: '2px 6px',
      borderRadius: '32px',
      border: '1px solid var(--superblocks-accent-blue-subtle)',
      background: 'var(--superblocks-accent-blue-subtle)',
      height: '20px',
      width: `${Math.random() * 100 + 80}px`
    }}>
              <div style={{
      height: '12px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      width: '100%'
    }}></div>
            </div>)}
        </div>
      </div>;
  }
  if (error) {}
  const displayedActions = showAll ? actions : actions.slice(0, maxActions);
  const hiddenCount = actions.length - maxActions;
  return <div>
      {actions.length === 0 && <p style={{
    color: 'red',
    fontWeight: 'bold'
  }}>
          No actions found for {integrationId}. Check console for errors.
        </p>}
      <div style={{
    display: 'inline-flex',
    flexWrap: 'wrap',
    gap: '0.5em',
    marginTop: '8px'
  }}>
        {displayedActions.map((action, index) => <div key={index} style={{
    alignItems: 'center',
    padding: '4px 8px',
    borderRadius: '32px',
    border: '1px solid var(--superblocks-accent-blue-subtle)',
    background: 'var(--superblocks-accent-blue-subtle)',
    transition: 'display 0.3ms ease-in'
  }}>
            <h4 style={{
    color: 'var(--ifm-color-primary-darker)',
    marginBottom: 'unset',
    alignSelf: 'center',
    fontSize: '12px',
    fontStyle: 'normal',
    fontWeight: '500',
    lineHeight: '16px',
    margin: '0',
    padding: '0'
  }}>
              {action.summary}
            </h4>
          </div>)}
        {showAll && <a href={`https://app.superblocks.com/integrations/${integrationId === 'openai' ? 'openai_v2' : integrationId}`} target="_blank" rel="noopener noreferrer" style={{
    color: 'var(--ifm-color-primary)',
    fontSize: '12px',
    textDecoration: 'none',
    border: 'none',
    borderBottom: 'none',
    padding: '4px 0',
    marginLeft: '8px',
    alignSelf: 'center',
    whiteSpace: 'nowrap'
  }}>
            See full list in app →
          </a>}
      </div>
      
      {!showAll && hiddenCount > 0 && <div style={{
    marginTop: '8px',
    textAlign: 'center'
  }}>
          <button onClick={() => setShowAll(true)} style={{
    background: 'none',
    border: 'none',
    color: '#6c7989',
    cursor: 'pointer',
    textDecoration: 'underline',
    fontSize: '14px'
  }}>
            and {hiddenCount} more
          </button>
        </div>}
      
      {showAll && hiddenCount > 0 && <div style={{
    marginTop: '8px',
    textAlign: 'center'
  }}>
          <button onClick={() => setShowAll(false)} style={{
    background: 'none',
    border: 'none',
    color: '#6c7989',
    cursor: 'pointer',
    textDecoration: 'underline',
    fontSize: '14px'
  }}>
            show less
          </button>
        </div>}
    </div>;
};

export const DynamicIntegrationSetup = ({integrationId, title}) => {
  const [setupContent, setSetupContent] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  useEffect(() => {
    const fetchIntegrationSpec = async () => {
      try {
        setLoading(true);
        const response = await fetch(`https://integrations.superblocks.com/openapi/${integrationId}.yaml`);
        if (!response.ok) {
          throw new Error(`Failed to fetch integration spec: ${response.status}`);
        }
        const yamlText = await response.text();
        const infoStartIndex = yamlText.indexOf('\ninfo:');
        if (infoStartIndex === -1) {
          const altIndex = yamlText.indexOf('info:');
          if (altIndex !== 0) {
            throw new Error('Could not find info section in YAML');
          }
        }
        let afterInfo, infoSection;
        if (infoStartIndex !== -1) {
          afterInfo = yamlText.slice(infoStartIndex + 6);
        } else {
          afterInfo = yamlText.slice(5);
        }
        const lines = afterInfo.split('\n');
        let infoEndIndex = afterInfo.length;
        for (let i = 0; i < lines.length; i++) {
          const line = lines[i];
          if (line.trim() && !line.match(/^\s/)) {
            infoEndIndex = afterInfo.indexOf(line);
            break;
          }
        }
        infoSection = afterInfo.slice(0, infoEndIndex).trim();
        let overviewText = null;
        const multilineMatchDash = infoSection.match(/description:\s*\|\-\s*\n([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
        if (multilineMatchDash) {
          overviewText = multilineMatchDash[1].replace(/^\s+|\s+$/g, '');
        } else {
          const multilineMatch = infoSection.match(/description:\s*\|\s*\n([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
          if (multilineMatch) {
            overviewText = multilineMatch[1].replace(/^\s+|\s+$/g, '');
          } else {
            const simpleMatch = infoSection.match(/description:\s*([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
            if (simpleMatch) {
              overviewText = simpleMatch[1].trim();
            }
          }
        }
        if (!overviewText) {
          throw new Error('Could not extract description/overview from YAML spec');
        }
        const parsedOverview = parseOverview(overviewText);
        if (!parsedOverview.setup) {
          throw new Error('Could not parse setup section from overview');
        }
        const titleMatch = infoSection.match(/title:\s*(.+)/);
        const specTitle = titleMatch ? titleMatch[1].trim() : title;
        const content = `${parsedOverview.setup}

### Add integration
Once you have an access token, you're ready to set up your Superblocks integration.

1. In the web app, navigate to the [Integrations](https://app.superblocks.com/integrations) page
2. Click on the **${specTitle}** tile
3. **Name** the integration 
4. Paste your credentials into the relevant fields
5. Optionally, add more configurations to set credentials for [different environments](/development-lifecycle/build/profiles)
6. Click **Create**`;
        setSetupContent(content);
        setError(null);
      } catch (err) {
        setError(err.message);
        setSetupContent(null);
      } finally {
        setLoading(false);
      }
    };
    fetchIntegrationSpec();
  }, [integrationId, title]);
  const parseOverview = overview => {
    const startIndex = overview.indexOf('## Get started');
    if (startIndex === -1) {
      const nextSectionMatch = overview.match(/\n##\s+/);
      if (!nextSectionMatch) {
        return {
          description: overview.trim(),
          setup: null,
          usage: null
        };
      } else {
        const nextSectionIndex = nextSectionMatch.index;
        return {
          description: overview.slice(0, nextSectionIndex).trim(),
          setup: null,
          usage: overview.slice(nextSectionIndex).trim()
        };
      }
    }
    const lines = overview.split('\n');
    let getStartedLineIndex = -1;
    for (let i = 0; i < lines.length; i++) {
      if (lines[i].trim().startsWith('## Get started')) {
        getStartedLineIndex = i;
        break;
      }
    }
    if (getStartedLineIndex === -1) {
      return {
        description: overview.slice(0, startIndex).trim(),
        setup: overview.slice(startIndex).replace('## Get started', '### Create an access token').trim(),
        usage: null
      };
    }
    let nextSectionLineIndex = -1;
    for (let i = getStartedLineIndex + 1; i < lines.length; i++) {
      const trimmedLine = lines[i].trim();
      if (trimmedLine.startsWith('##') && !trimmedLine.includes('Get started')) {
        nextSectionLineIndex = i;
        break;
      }
    }
    let endIndex;
    if (nextSectionLineIndex === -1) {
      endIndex = overview.length;
    } else {
      let currentPos = 0;
      for (let i = 0; i < nextSectionLineIndex; i++) {
        currentPos += lines[i].length + 1;
      }
      endIndex = currentPos;
    }
    return {
      description: overview.slice(0, startIndex).trim(),
      setup: overview.slice(startIndex, endIndex).replace('## Get started', '### Create an access token').trim(),
      usage: overview.slice(endIndex).trim()
    };
  };
  if (loading) {
    return <div>
        <div style={{
      height: '20px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '16px',
      width: '40%'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      width: '60%'
    }}></div>
      </div>;
  }
  if (error) {
    return <div style={{
      padding: '12px',
      backgroundColor: '#fee',
      border: '1px solid #fcc',
      borderRadius: '4px',
      color: '#c00'
    }}>
        <strong>Error parsing setup:</strong> {error}
      </div>;
  }
  if (!setupContent) {
    return <div style={{
      padding: '12px',
      backgroundColor: '#fee',
      border: '1px solid #fcc',
      borderRadius: '4px',
      color: '#c00'
    }}>
        <strong>Error:</strong> No setup content found for {integrationId}
      </div>;
  }
  const renderMarkdown = content => {
    const lines = content.split('\n');
    const elements = [];
    let currentOrderedList = [];
    let currentUnorderedList = [];
    let lastOrderedItemIndex = -1;
    let lastElementWasHeader = false;
    for (let i = 0; i < lines.length; i++) {
      const originalLine = lines[i];
      const line = originalLine.trim();
      const indentLevel = originalLine.length - originalLine.trimStart().length;
      if (!line) {
        if (currentOrderedList.length > 0) {
          elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
          currentOrderedList = [];
          lastOrderedItemIndex = -1;
          lastElementWasHeader = false;
        }
        if (currentUnorderedList.length > 0) {
          elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
          currentUnorderedList = [];
          lastElementWasHeader = false;
        }
        let nextNonEmptyLine = null;
        for (let j = i + 1; j < lines.length; j++) {
          const nextLine = lines[j].trim();
          if (nextLine) {
            nextNonEmptyLine = nextLine;
            break;
          }
        }
        const isHeader = nextNonEmptyLine && (nextNonEmptyLine.startsWith('# ') || nextNonEmptyLine.startsWith('## ') || nextNonEmptyLine.startsWith('### '));
        const isLearnMore = nextNonEmptyLine && nextNonEmptyLine.toLowerCase().includes('learn more about');
        const noteCheckText = nextNonEmptyLine.replace(/\*\*/g, '').toLowerCase();
        const isNote = nextNonEmptyLine && (noteCheckText.startsWith('note:') || noteCheckText.includes('note:'));
        if (isNote) {
          elements.push(<br key={`br-before-note-${i}`} />);
        } else if (!lastElementWasHeader && !isHeader && !isLearnMore) {
          elements.push(<br key={`br-${i}`} />);
        }
        continue;
      }
      if (line.startsWith('### ')) {
        if (currentOrderedList.length > 0) {
          elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
          currentOrderedList = [];
          lastOrderedItemIndex = -1;
        }
        if (currentUnorderedList.length > 0) {
          elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
          currentUnorderedList = [];
        }
        elements.push(<h3 key={`h3-${i}`}>{line.substring(4)}</h3>);
        lastElementWasHeader = true;
        continue;
      }
      if (line.startsWith('## ')) {
        if (currentOrderedList.length > 0) {
          elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
          currentOrderedList = [];
          lastOrderedItemIndex = -1;
        }
        if (currentUnorderedList.length > 0) {
          elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
          currentUnorderedList = [];
        }
        elements.push(<h2 key={`h2-${i}`}>{line.substring(3)}</h2>);
        lastElementWasHeader = true;
        continue;
      }
      if (line.startsWith('# ')) {
        if (currentOrderedList.length > 0) {
          elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
          currentOrderedList = [];
          lastOrderedItemIndex = -1;
        }
        if (currentUnorderedList.length > 0) {
          elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
          currentUnorderedList = [];
        }
        elements.push(<h1 key={`h1-${i}`}>{line.substring(2)}</h1>);
        lastElementWasHeader = true;
        continue;
      }
      const trimmedLineForOrdered = line.trimStart();
      if (trimmedLineForOrdered.match(/^\d+\.\s/)) {
        if (currentUnorderedList.length > 0 && lastOrderedItemIndex >= 0) {
          const nestedList = <ul key={`nested-ul-${i}`}>{currentUnorderedList}</ul>;
          const lastItem = currentOrderedList[lastOrderedItemIndex];
          currentOrderedList[lastOrderedItemIndex] = <li key={`li-nested-${lastOrderedItemIndex}`}>
              <span dangerouslySetInnerHTML={{
            __html: lastItem.props.dangerouslySetInnerHTML.__html
          }} />
              {nestedList}
            </li>;
          currentUnorderedList = [];
        }
        const listItem = trimmedLineForOrdered.replace(/^\d+\.\s/, '');
        const processedItem = processInlineMarkdown(listItem);
        currentOrderedList.push(<li key={`li-${i}`} dangerouslySetInnerHTML={{
          __html: processedItem
        }} />);
        lastOrderedItemIndex = currentOrderedList.length - 1;
        lastElementWasHeader = false;
        continue;
      }
      const trimmedLine = line.trimStart();
      if (trimmedLine.startsWith('- ') || trimmedLine.startsWith('* ')) {
        if (indentLevel > 0 && currentOrderedList.length > 0) {
          const listItem = trimmedLine.substring(2);
          const processedItem = processInlineMarkdown(listItem);
          currentUnorderedList.push(<li key={`li-${i}`} dangerouslySetInnerHTML={{
            __html: processedItem
          }} />);
          lastElementWasHeader = false;
          continue;
        }
        const listItem = trimmedLine.substring(2);
        const processedItem = processInlineMarkdown(listItem);
        currentUnorderedList.push(<li key={`li-${i}`} dangerouslySetInnerHTML={{
          __html: processedItem
        }} />);
        lastElementWasHeader = false;
        continue;
      }
      if (currentUnorderedList.length > 0 && lastOrderedItemIndex >= 0 && currentOrderedList.length > 0) {
        const nestedList = <ul key={`nested-ul-${i}`}>{currentUnorderedList}</ul>;
        const lastItem = currentOrderedList[lastOrderedItemIndex];
        currentOrderedList[lastOrderedItemIndex] = <li key={`li-nested-${lastOrderedItemIndex}`}>
            <span dangerouslySetInnerHTML={{
          __html: lastItem.props.dangerouslySetInnerHTML.__html
        }} />
            {nestedList}
          </li>;
        currentUnorderedList = [];
        lastOrderedItemIndex = -1;
      }
      if (currentOrderedList.length > 0) {
        elements.push(<ol key={`ol-${i}`}>{currentOrderedList}</ol>);
        currentOrderedList = [];
        lastOrderedItemIndex = -1;
      }
      if (currentUnorderedList.length > 0) {
        elements.push(<ul key={`ul-${i}`}>{currentUnorderedList}</ul>);
        currentUnorderedList = [];
      }
      const processedLine = processInlineMarkdown(line);
      elements.push(<p key={`p-${i}`} dangerouslySetInnerHTML={{
        __html: processedLine
      }} />);
      lastElementWasHeader = false;
    }
    if (currentUnorderedList.length > 0 && lastOrderedItemIndex >= 0 && currentOrderedList.length > 0) {
      const nestedList = <ul key="nested-ul-final">{currentUnorderedList}</ul>;
      const lastItem = currentOrderedList[lastOrderedItemIndex];
      currentOrderedList[lastOrderedItemIndex] = <li key={`li-nested-final-${lastOrderedItemIndex}`}>
          <span dangerouslySetInnerHTML={{
        __html: lastItem.props.dangerouslySetInnerHTML.__html
      }} />
          {nestedList}
        </li>;
      currentUnorderedList = [];
    }
    if (currentOrderedList.length > 0) {
      elements.push(<ol key="ol-final">{currentOrderedList}</ol>);
    }
    if (currentUnorderedList.length > 0) {
      elements.push(<ul key="ul-final">{currentUnorderedList}</ul>);
    }
    return elements;
  };
  const processInlineMarkdown = text => {
    return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>').replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>').replace(/`([^`]+)`/g, '<code>$1</code>');
  };
  return <div>
      {setupContent && renderMarkdown(setupContent)}
    </div>;
};

export const DynamicIntegrationOverview = ({integrationId}) => {
  const [overview, setOverview] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  useEffect(() => {
    const fetchIntegrationSpec = async () => {
      try {
        setLoading(true);
        const response = await fetch(`https://integrations.superblocks.com/openapi/${integrationId}.yaml`);
        if (!response.ok) {
          throw new Error(`Failed to fetch integration spec: ${response.status}`);
        }
        const yamlText = await response.text();
        const infoStartIndex = yamlText.indexOf('\ninfo:');
        if (infoStartIndex === -1) {
          const altIndex = yamlText.indexOf('info:');
          if (altIndex === 0) {
            const afterInfo = yamlText.slice(5);
            const lines = afterInfo.split('\n');
            let infoEndIndex = afterInfo.length;
            for (let i = 0; i < lines.length; i++) {
              const line = lines[i];
              if (line.trim() && !line.match(/^\s/)) {
                infoEndIndex = afterInfo.indexOf(line);
                break;
              }
            }
            var infoSection = afterInfo.slice(0, infoEndIndex).trim();
          } else {
            throw new Error('Could not find info section in YAML');
          }
        } else {
          const afterInfo = yamlText.slice(infoStartIndex + 6);
          const lines = afterInfo.split('\n');
          let infoEndIndex = afterInfo.length;
          for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            if (line.trim() && !line.match(/^\s/)) {
              infoEndIndex = afterInfo.indexOf(line);
              break;
            }
          }
          var infoSection = afterInfo.slice(0, infoEndIndex).trim();
        }
        let overviewText = null;
        const multilineMatchDash = infoSection.match(/description:\s*\|\-\s*\n([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
        if (multilineMatchDash) {
          overviewText = multilineMatchDash[1].trim();
        } else {
          const multilineMatch = infoSection.match(/description:\s*\|\s*\n([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
          if (multilineMatch) {
            overviewText = multilineMatch[1].trim();
          } else {
            const simpleMatch = infoSection.match(/description:\s*([\s\S]*?)(?=\n\s+(?:termsOfService|version|contact|license|x-public-description):|$)/);
            if (simpleMatch) {
              overviewText = simpleMatch[1].trim();
            }
          }
        }
        if (!overviewText) {
          throw new Error('Could not extract description/overview from YAML spec');
        }
        const parsedOverview = parseOverview(overviewText);
        if (!parsedOverview.description) {
          throw new Error('Could not parse description from overview');
        }
        setOverview({
          description: parsedOverview.description
        });
        setError(null);
      } catch (err) {
        setError(err.message);
        setOverview(null);
      } finally {
        setLoading(false);
      }
    };
    fetchIntegrationSpec();
  }, [integrationId]);
  const parseOverview = overview => {
    const HEADER_OFFSET = 3;
    const startIndex = overview.indexOf('## Get started');
    let endIndex = overview.slice(startIndex + HEADER_OFFSET).indexOf("\n##");
    if (startIndex === -1 && endIndex === -1) {
      return {
        description: overview.trim(),
        setup: null,
        usage: null
      };
    } else if (startIndex === -1) {
      endIndex = endIndex + startIndex + HEADER_OFFSET;
      return {
        description: overview.slice(0, endIndex).trim(),
        setup: null,
        usage: overview.slice(endIndex).trim()
      };
    } else if (endIndex === -1) {
      return {
        description: overview.slice(0, startIndex).trim(),
        setup: overview.slice(startIndex).replace('## Get started', '### Create an access token').trim(),
        usage: null
      };
    } else {
      endIndex = endIndex + startIndex + HEADER_OFFSET;
      return {
        description: overview.slice(0, startIndex).trim(),
        setup: overview.slice(startIndex, endIndex).replace('## Get started', '### Create an access token').trim(),
        usage: overview.slice(endIndex).trim()
      };
    }
  };
  if (loading) {
    return <div>
        <div style={{
      height: '20px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '16px',
      width: '60%'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      marginBottom: '8px',
      width: '80%'
    }}></div>
        <div style={{
      height: '16px',
      backgroundColor: '#e1e5e9',
      borderRadius: '4px',
      width: '70%'
    }}></div>
      </div>;
  }
  if (error) {
    return <div style={{
      padding: '12px',
      backgroundColor: '#fee',
      border: '1px solid #fcc',
      borderRadius: '4px',
      color: '#c00'
    }}>
        <strong>Error parsing overview:</strong> {error}
      </div>;
  }
  if (!overview || !overview.description) {
    return <div style={{
      padding: '12px',
      backgroundColor: '#fee',
      border: '1px solid #fcc',
      borderRadius: '4px',
      color: '#c00'
    }}>
        <strong>Error:</strong> No overview description found for {integrationId}
      </div>;
  }
  return <div>
      <p>{overview.description}</p>
    </div>;
};

export const Alert = ({type, title, children}) => {
  const getIcon = () => {
    switch (type) {
      case 'info':
        return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm0 15c-.552 0-1-.448-1-1s.448-1 1-1 1 .448 1 1-.448 1-1 1zm1-3H9V6h2v6z' fill='%230099FF'/%3E%3C/svg%3E";
      case 'success':
        return "data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm4.293 6.293L9 11.586 5.707 8.293c-.391-.391-1.024-.391-1.414 0s-.391 1.024 0 1.414l4 4c.391.391 1.024.391 1.414 0l6-6c.391-.391.391-1.024 0-1.414s-1.024-.391-1.414 0z' fill='%230CC26D'/%3E%3C/svg%3E";
      case 'warning':
        return "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHhtbDpzcGFjZT0ncHJlc2VydmUnIHdpZHRoPScxMDgwJyBoZWlnaHQ9JzEwODAnPjxyZWN0IHdpZHRoPScxMDAlJyBoZWlnaHQ9JzEwMCUnIGZpbGw9J3RyYW5zcGFyZW50Jy8+PHBhdGggZD0nTTEzLjc5NCAxMC43NSA4LjMgMS4yNWExLjUgMS41IDAgMCAwLTIuNiAwbC01LjQ5NCA5LjVBMS40OTQgMS40OTQgMCAwIDAgMS41IDEzaDExYTEuNDkzIDEuNDkzIDAgMCAwIDEuMjk0LTIuMjVNNi41IDUuNWEuNS41IDAgMCAxIDEgMFY4YS41LjUgMCAwIDEtMSAwek03IDExYS43NS43NSAwIDEgMSAwLTEuNS43NS43NSAwIDAgMSAwIDEuNScgc3R5bGU9J3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDoxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7ZmlsbDojZmY5ZjM1O2ZpbGwtcnVsZTpub256ZXJvO29wYWNpdHk6MScgdHJhbnNmb3JtPSd0cmFuc2xhdGUoLjAyIDE5LjMwNSlzY2FsZSg3Ny4xNCknLz48L3N2Zz4=";
      case 'danger':
        return "data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm5.707 4.293L10 9.586 4.293 4.293c-.391-.391-1.024-.391-1.414 0s-.391 1.024 0 1.414L8.586 11l-5.707 5.293c-.391.391-.391 1.024 0 1.414s1.024.391 1.414 0L10 12.414l5.707 5.293c.391.391 1.024.391 1.414 0s.391-1.024 0-1.414L11.414 11l5.707-5.293c.391-.391.391-1.024 0-1.414s-1.024-.391-1.414 0z' fill='%23F45252'/%3E%3C/svg%3E";
      case 'note':
        return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm0 15c-.552 0-1-.448-1-1s.448-1 1-1 1 .448 1 1-.448 1-1 1zm1-3H9V6h2v6z' fill='%230099FF'/%3E%3C/svg%3E";
      default:
        return "";
    }
  };
  return <div className={`alert alert--${type}`}>
      <div className="alert-icon" style={{
    backgroundImage: `url("${getIcon()}")`,
    backgroundRepeat: 'no-repeat',
    backgroundPosition: 'center center',
    backgroundSize: '20px',
    width: '24px',
    height: '24px',
    position: 'absolute',
    left: '16px',
    top: '16px'
  }} />
      <div className="alert-content">
        {title && <div className="alert-title">{title}</div>}
        <div className="alert-body">{children}</div>
      </div>
    </div>;
};

<DynamicIntegrationOverview integrationId="bitbucket" />

## Setting up Bitbucket

<DynamicIntegrationSetup integrationId="bitbucket" title="Bitbucket" />

<Alert type="success">
  <IntegrationSuccess integrationName="Bitbucket" />
</Alert>

## Use Bitbucket in APIs

Once your Bitbucket integration is created, you can start calling Bitbucket actions in Superblocks APIs. In addition to generic HTTP requests, Bitbucket supports the following actions.

### Supported actions

<DynamicIntegrationActions integrationId="bitbucket" maxActions={15} />
